How to round 2100.825 to 2100.83?

polaris · · 731 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Hi,</p> <p>I try to round 2100.825 to 2100.83 but get always 2100.82. Here is the code </p> <pre><code>package main import ( &#34;fmt&#34; &#34;math&#34; ) func main() { fmt.Println(&#34;Expecting 2100.83, but was:&#34;, round100(2100.825)) } func round100(x float64) float64 { a := x * 100 _, rem := math.Modf(a) fmt.Println(&#34;frac:&#34;, rem) if rem &gt;= 0.5 { fmt.Println(&#34;ceil&#34;) a = math.Ceil(a) } else { fmt.Println(&#34;floor&#34;) a = math.Floor(a) } return a / 100 } </code></pre> <p>If I modify the number to 1100.825 it is properly rounded to 1100.83.</p> <p>Where is the flaw? And what is the correct implementation of round100?</p> <hr/>**评论:**<br/><br/>ElliottStoneham: <pre><p>The problem is not with your program, it is that the float64 representation of 2100.825 is not exact. Try:</p> <pre><code>fmt.Printf(&#34;%4.24g&#34;,2100.825) </code></pre></pre>coldDragonBreath: <pre><p>For the lazy <a href="https://play.golang.org/p/YBJeUP73tp" rel="nofollow">playground</a>:</p> <p><em>fmt.Printf(&#34;%4.24g&#34;,2100.825)</em> yields <em>2100.82499999999981810106</em></p></pre>pdq: <pre><p>Are you trying to work with currency or decimal values?</p> <p>If so, I&#39;d strongly suggest to stay away from floating point (which rounds) and use a decimal-type struct instead, like <a href="https://github.com/shopspring/decimal">https://github.com/shopspring/decimal</a></p></pre>a4st: <pre><p>Yes, you are right, I&#39;m working with currency. I will try out the library you suggest. But I&#39;m still interested why is it only about this &#34;magic&#34; number?</p></pre>coldDragonBreath: <pre><p>It&#39;s the classic Programming showcase of why: (0.1 + 0.2 != 0.3)</p> <p>If you want to get into the maths behind it, google and search &#34;IEEE 754&#34;</p></pre>thockin: <pre><p>Yeah, don&#39;t use FP for currency. Use the smallest integral unit you care about. E.g. cents or millicents or microcents or ..</p></pre>tmornini: <pre><p>A good decimal package is a far better idea than using integers.</p></pre>a4st: <pre><p>What about using math/big.Rat?</p></pre>joushou: <pre><p>TL;DR: Never use floating point if rounding errors (small or large) are a concern. Accurate math requires integers and safe integer operations (addition, subtraction, multiplication).</p> <p>Floating point can only represent some values precisely due to its design. In its simplified form, a floating point value is calculated as s * b<sup>e,</sup> where s is the significand, e is the exponent and b is the base. In our case, the base is two, turning it the equation into s * 2<sup>e.</sup> You can&#39;t express the value 1/3 using when the base is 2, although you can get awfully close if with some crazy large and obscure values of s and e.</p> <p>Also note that using integer operands is not enough, but you may only use &#34;safe&#34; integer operations if you need to maintain an accurate value. A division of integer operations always rounds towards zero—that is, 2/3 yields 0—which is basically a guaranteed rounding error. Likewise, casting a floating point value to an integer also round towards zero.</p></pre>tmornini: <pre><blockquote> <p>Never use floating point if rounding errors (small or large) are a concern. Accurate math requires integers and safe integer operations</p> </blockquote> <p>Or, better yet, a decimal package.</p></pre>tmornini: <pre><p>The way floating point values work is that they take a big number range, and break it up into as many buckets as there are bits in the representation.</p> <p>So, as a thought experiment -- because this is NOT the way it&#39;s actually done, simplification to aid in understanding -- imagine you had a 3 bit floating point number that represented values between -75 and 75.</p> <p>The first bit represent positive or negative. I&#39;ll use 0 for positive values, 1 for negative values.</p> <p>The ONLY values representable would be:</p> <pre><code>011 = 75.0 010 = 50.0 001 = 25.0 000 = 0.0 100 = -18.75 101 = -37.5 110 = -56.25 111 = -75.0 </code></pre> <p>And here&#39;s the point: those are the ONLY numbers that can be represented in this &#34;floating point&#34; system.</p> <p>If you use 32 bit floats, 2<sup>32</sup> numbers are representable. With 64 bit floats, 2<sup>64</sup> numbers are representable.</p> <p>As far as floats are concerns, all numbers that do not happen to be represented, simply don&#39;t exist!</p> <p>Here&#39;s a concrete example in the <a href="https://play.golang.org/p/-oENvtuLSB" rel="nofollow">Go Playground</a></p></pre>mjibson: <pre><p>In addition to the shopspring package already posted, <a href="https://godoc.org/github.com/cockroachdb/apd" rel="nofollow">apd</a> is another decimal package that does <a href="https://godoc.org/github.com/cockroachdb/apd#example-Context-Quantize" rel="nofollow">similar things</a>, but also supports various <a href="https://godoc.org/github.com/cockroachdb/apd#pkg-constants" rel="nofollow">kinds of rounding</a>.</p></pre>gobdgobd: <pre><p>Seems to work for me if I add this after your Modf line</p> <pre><code>rem, _ = strconv.ParseFloat(fmt.Sprintf(&#34;%.1f&#34;, rem), 64) </code></pre></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

731 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传