Ruby从23 + 9.23 = 32.230000000000004看起来看似不稳定

And*_*hin 2 ruby ruby-on-rails

对于Ruby,我收到了非常有趣且看似不稳定的结果.到底是怎么回事?

这是对的!

>> 23+9.22 
=> 32.22
Run Code Online (Sandbox Code Playgroud)

这不是!

>> 23+9.23 
=> 32.230000000000004
Run Code Online (Sandbox Code Playgroud)

所有尾随的零都来自哪里?到底是怎么回事?

Phr*_*ogz 9

puts "%.30f" % 9.23
#=> 9.230000000000000426325641456060
Run Code Online (Sandbox Code Playgroud)

由于base-2(数字的计算机内部表示)和base-10(您习惯于处理和键入文本编辑器或IRB)之间的区别,您不能将9.23 完全表示为浮点值.这不是Ruby特有的,但几乎存在于每种编程语言中.

例如,9.23在内部表示为2的幂的总和,如:

01001.00111010111...
||||| ||||||||||+-> 1 * 1/2048 \            
||||| |||||||||+--> 1 * 1/1024  \          
||||| ||||||||+---> 1 * 1/512    \        
||||| |||||||+----> 0 * 1/256     \       
||||| ||||||+-----> 1 * 1/128      \      
||||| |||||+------> 0 * 1/64         fraction = 0.22998046875
||||| ||||+-------> 1 * 1/32       /     
||||| |||+--------> 1 * 1/16      /      
||||| ||+---------> 1 * 1/8      /      
||||| |+----------> 0 * 1/4     /       
||||| +-----------> 0 * 1/2    /        
|||||  
||||+-------------> 1 * 1      \
|||+--------------> 0 * 2       \  
||+---------------> 0 * 4         integer = 9
|+----------------> 1 * 8       /
+-----------------> 0 * 16     /
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请阅读每位计算机科学家应该了解的关于浮点算术的内容以及维基百科上IEEE 754描述.

当精确度很重要时(例如货币),您的解决方法是:

  1. 跟踪计算的最小可用单位(例如便士)并仅转换为十进制显示.例如:

    result = 2300 + 923          # $23.00 + $9.23
    puts "%.2f" % (result/100.0) # Ensure output rounded to two decimal places
    #=> 32.23
    
    Run Code Online (Sandbox Code Playgroud)
  2. 使用类似于BigDecimal跟踪精确精度的库,并将数据库设置为使用DECIMAL或NUMERIC等固定精度数据类型.

  3. 接受数值通常"足够接近",并且在向用户显示时始终使用格式化选项,例如

    result = 23 + 9.27    #=> 32.269999999999996
    puts "%g" % result    #=> 32.27
    puts "%.3f" % result  #=> 32.270
    puts "%.1f" % result  #=> 32.3
    
    Run Code Online (Sandbox Code Playgroud)