除法和乘法后浮点数不相等 [英] Float not equal after division followed by multiplication

查看:48
本文介绍了除法和乘法后浮点数不相等的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在测试一个我用 Ruby 制作的小而简单的库.目标是将 EUR 转换为 CNY,反之亦然.简单.

我对其进行了测试以确保一切正常,但出现了意外问题.当我使用 to_euro 后跟 to_yuan 时,它应该回到原来的 amount ;它不会发生.我尝试 .to_fround(2) 修复一些测试的 amount 变量,提出新的测试,但它永远不等于我全球范围内期待;我已经没有办法解决这个问题了:(

class 货币attr_reader :金额,:货币def 初始化(金额,货币 =欧元")@金额=金额@currency = 货币结尾def to_yuanupdate_currency!('CNY', amount * Settings.instance.exchange_rate_to_yuan)结尾def to_euroupdate_currency!('EUR', 金额/Settings.instance.exchange_rate_to_yuan)结尾定义显示"%.2f #{current_symbol}" % 数量结尾私人的def current_symbol如果货币 == '欧元'符号 = Settings.instance.supplier_currency.symbolelsif 货币 == 'CNY'符号 = Settings.instance.platform_currency.symbol结尾结尾def update_currency!(new_currency, new_amount)除非新货币 == 货币@currency = new_currency@amount = new_amount结尾自己结尾结尾

测试

describe Currency dolet(:rate) { Settings.instance.exchange_rate_to_yuan.to_f }上下文#to_yuan"做它应该返回货币对象"做期望(货币.新(20).to_yuan).to be_a(货币)结尾它应该转换成人民币"吗期望(Currency.new(20).to_yuan.amount).to eql(20.00 * rate)结尾它应该转换成欧元再转换成人民币"做# 状态数据测试货币 = Currency.new(150, 'CNY')期望(货币.to_euro).to be_a(货币)期望(货币.to_yuan).to be_a(货币)期望(货币.金额).到 eql(150.00)结尾结尾上下文#to_euro"做它应该转换为欧元"做期望(Currency.new(150,'CNY').to_euro.amount).to eql(150/rate)结尾结尾上下文#display"做它应该显示欧元"做期望(Currency.new(10,'EUR').显示).to eql(10.00€")结尾它应该显示元"做expect(Currency.new(60.50, 'CNY').display).to eql("60.50 ¥")结尾结尾结尾

这是我的 RSpec 结果

我很确定这个问题很常见,知道如何轻松解决吗?

解决方案

Float 不是精确的数字表示,如 ruby 文档:

<块引用>

Float 对象使用原生架构的双精度浮点表示法来表示不精确的实数.

这不是 ruby​​ 错误,因为浮点数只能由固定数量的字节表示,因此无法正确存储十进制数.

或者,您可以使用 ruby​​ RationalBigDecimal

在处理货币和货币兑换时,使用 money gem 也很常见.>

I'm testing a small and simple library I made in Ruby. The goal is to convert from EUR to CNY and vice versa. Simple.

I tested it to be sure everything works but I got an unexpected issue. When I use to_euro followed by to_yuan it should go back to the original amount ; it doesn't happen. I tried to .to_f or round(2) the amount variable which fix some tests, raise new ones, but it's never equal to what I expect globally ; I'm running out of idea to fix this :(

class Currency

  attr_reader :amount, :currency

  def initialize(amount, currency='EUR')
    @amount = amount
    @currency = currency
  end

  def to_yuan
    update_currency!('CNY', amount * Settings.instance.exchange_rate_to_yuan)
  end

  def to_euro
    update_currency!('EUR', amount / Settings.instance.exchange_rate_to_yuan)
  end

  def display
    "%.2f #{current_symbol}" % amount
  end

  private

  def current_symbol
    if currency == 'EUR'
      symbol = Settings.instance.supplier_currency.symbol
    elsif currency == 'CNY'
      symbol = Settings.instance.platform_currency.symbol
    end
  end

  def update_currency!(new_currency, new_amount)
    unless new_currency == currency
      @currency = new_currency
      @amount = new_amount
    end
    self
  end

end

Tests

describe Currency do

  let(:rate) { Settings.instance.exchange_rate_to_yuan.to_f }

  context "#to_yuan" do

    it "should return Currency object" do
      expect(Currency.new(20).to_yuan).to be_a(Currency)
    end

    it "should convert to yuan" do
      expect(Currency.new(20).to_yuan.amount).to eql(20.00 * rate)
    end

    it "should convert to euro and back to yuan" do
      # state data test
      currency = Currency.new(150, 'CNY')
      expect(currency.to_euro).to be_a(Currency)
      expect(currency.to_yuan).to be_a(Currency)
      expect(currency.amount).to eql(150.00)
    end

  end

  context "#to_euro" do

    it "should convert to euro" do
      expect(Currency.new(150, 'CNY').to_euro.amount).to eql(150 / rate)
    end

  end

  context "#display" do

    it "should display euros" do
      expect(Currency.new(10, 'EUR').display).to eql("10.00 €")
    end

    it "should display yuan" do
      expect(Currency.new(60.50, 'CNY').display).to eql("60.50 ¥")
    end

  end


end

And here's my RSpec result

I'm pretty sure this problem is very common, any idea how to solve it easily ?

解决方案

Float isn't an exact number representation, as stated in the ruby docs:

Float objects represent inexact real numbers using the native architecture's double-precision floating point representation.

This not ruby fault, as floats can only be represented by a fixed number of bytes and therefor cannot store decimal numbers correctly.

Alternatively, you can use ruby Rational or BigDecimal

Its is also fairly common to use the money gem when dealing with currency and money conversion.

这篇关于除法和乘法后浮点数不相等的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆