Crystal: `Number.round` does not handle its arguments correctly

Created on 13 Feb 2018  路  3Comments  路  Source: crystal-lang/crystal

The relevant portion of the code:

https://github.com/crystal-lang/crystal/blob/e2a1389e8165fb097c785524337d7bb7b550a086/src/number.cr#L212-L221

For example:

n = 1.2345678901234567
puts n.round(9) # => 1.23456789
puts (n * 1e9).round / 1e9 # => 1.23456789
puts n.round(12) # => 1.2345678895572774
puts (n * 1e12).round / 1e12 # => 1.234567890123
puts n.round(8,16) # => -NaN

base ** digits is treated as an 32-bit integer when both base and digits are integers, and hence can overflow.

Moreover, it doesn't do any input checks on both base or digits, not even if they are integers. They don't make sense if they are non-integers, so they should either be converted or raised when this happens.

bug stdlib

Most helpful comment

In Crystal 0.31.1 it's

n = 1.2345678901234567
puts n.round(9) # => 1.23456789
puts (n * 1e9).round / 1e9 # => 1.23456789
puts n.round(12) # => 1.234567890123
puts (n * 1e12).round / 1e12 # => 1.234567890123
puts n.round(8,16) # => 1.2345678901765496
$ crystal --version
Crystal 0.31.1 (2019-10-02)

LLVM: 8.0.1
Default target: x86_64-apple-macosx

All 3 comments

Mostly related to decisions in #3103. Overflow semantics in crystal's compiler and stdlib are largely undefined and unsafe.

In Crystal 0.31.1 it's

n = 1.2345678901234567
puts n.round(9) # => 1.23456789
puts (n * 1e9).round / 1e9 # => 1.23456789
puts n.round(12) # => 1.234567890123
puts (n * 1e12).round / 1e12 # => 1.234567890123
puts n.round(8,16) # => 1.2345678901765496
$ crystal --version
Crystal 0.31.1 (2019-10-02)

LLVM: 8.0.1
Default target: x86_64-apple-macosx

Yes, it seems to be working fine. Let's close this. Please reopen if you think it's still not working well.

Was this page helpful?
0 / 5 - 0 ratings