big.Float.Cmp does not work as expected

573 views
Skip to first unread message

Santhosh Kumar T

unread,
Feb 14, 2021, 3:21:31 PM2/14/21
to golang-nuts
I created:
    one instance using big.NewFloat function
    another instance using big.Float.SetString method

when compared both these instances using big.Float.Cmp, it return non-zero


am I doing something wrong ?
can some one explain why Cmp returns non-zero ?

thanks
Santhosh


Kurtis Rader

unread,
Feb 14, 2021, 3:33:24 PM2/14/21
to Santhosh Kumar T, golang-nuts
The value 123.4 cannot be represented exactly as a float64. See https://golang.org/pkg/math/big/#NewFloat for the caveats involved in using NewFloat() or SetFloat64(). Passing a string representation allows the implementation to preserve the exact value without rounding.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/82e16a17-387e-41cb-a79c-723c2e8a772bn%40googlegroups.com.


--
Kurtis Rader
Caretaker of the exceptional canines Junior and Hank

Santhosh Kumar T

unread,
Feb 14, 2021, 4:19:42 PM2/14/21
to golang-nuts
When I print both values, they print exactly same. so I am assuming no precision lost for this specific example 123.4.
but still Cmp returns non-zero.

---
Santhosh

Dan Kortschak

unread,
Feb 14, 2021, 4:23:45 PM2/14/21
to golan...@googlegroups.com
On Sun, 2021-02-14 at 13:19 -0800, Santhosh Kumar T wrote:
> When I print both values, they print exactly same. so I am assuming
> no precision lost for this specific example 123.4.
> but still Cmp returns non-zero.

This is not a good assumption to make, and is refuted by the result of
Cmp.

https://play.golang.org/p/ROr6cHMzWIf




Kurtis Rader

unread,
Feb 14, 2021, 4:31:53 PM2/14/21
to Santhosh Kumar T, golang-nuts
On Sun, Feb 14, 2021 at 1:19 PM Santhosh Kumar T <santhos...@gmail.com> wrote:
When I print both values, they print exactly same. so I am assuming no precision lost for this specific example 123.4.
but still Cmp returns non-zero.

As Dan pointed out, that is an invalid assumption. The code that formats a float (whether big or float64) as a string is aware that the IEEE 754 binary format for floats cannot represent most values exactly and compensates accordingly. That way you get "3.5" rather than "3.499999999999999" for example.
 
On Monday, February 15, 2021 at 2:03:24 AM UTC+5:30 Kurtis Rader wrote:
The value 123.4 cannot be represented exactly as a float64. See https://golang.org/pkg/math/big/#NewFloat for the caveats involved in using NewFloat() or SetFloat64(). Passing a string representation allows the implementation to preserve the exact value without rounding.

On Sun, Feb 14, 2021 at 12:21 PM Santhosh Kumar T <santhos...@gmail.com> wrote:
I created:
    one instance using big.NewFloat function
    another instance using big.Float.SetString method

when compared both these instances using big.Float.Cmp, it return non-zero


am I doing something wrong ?
can some one explain why Cmp returns non-zero ?

Santhosh Kumar T

unread,
Feb 14, 2021, 4:33:41 PM2/14/21
to golang-nuts
now I understand it. why they are not same

but why f2 printed as 123.40000000000000000139
f2 is constructed using SetString method, so it should be accurate and printed as 123.40000000000000000000.

- Santhosh

Dan Kortschak

unread,
Feb 14, 2021, 4:57:31 PM2/14/21
to golan...@googlegroups.com
You can set your precision arbitrarily high, but you will still find a
point at which there are non-zero decimal digits.

https://play.golang.org/p/JYcAvXQPfeO

123.4 cannot be represented in binary with a finite number of bits.

roger peppe

unread,
Feb 15, 2021, 4:34:23 AM2/15/21
to Santhosh Kumar T, golang-nuts
Thanks for bringing this up. It surprised me too until I realised what was going on.

The issue here is about the default precision that you're getting for those big.Float values.
When you use big.NewFloat, the precision you get is exactly the same as that of a base64 float (53 bits).
When you use SetString, you're getting 64 bits. If you change both float values to use the same
precision, you'll see a difference in representation: https://play.golang.org/p/4tBpBsPgGtE

This is documented, although arguably not that easy to find:

For the precision used by big.NewFloat:

NewFloat allocates and returns a new Float set to x, with precision 53
 
For the precision set by SetString:

If z's precision is 0, it is changed to 64 before rounding takes effect.

For the string output when used with fmt.Print:

The 'v' format is handled like 'g'. 
 
A negative precision selects the smallest number of decimal digits necessary to identify the value x uniquely using x.Prec() mantissa bits. 

Actually I don't think the docs explicitly say that a missing precision for %g is treated as a negative precision passed to the Text method, which could be considered a flaw in the docs.

 One other thing to be aware of: the String method doesn't do quite what you might expect: https://github.com/golang/go/issues/42887

  cheers,
    rog.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

Jesper Louis Andersen

unread,
Feb 15, 2021, 11:30:17 AM2/15/21
to Santhosh Kumar T, golang-nuts
On Sun, Feb 14, 2021 at 9:20 PM Santhosh Kumar T <santhos...@gmail.com> wrote:
I created:
    one instance using big.NewFloat function
    another instance using big.Float.SetString method


Adding to this:

Generally, comparing FP values for equality can give results you don't expect due to the way numerics for floating point values work. They almost behave like normal numbers. In particular, you would like laws such as

(x + y) + z = x + (y + z)
(x * y) * z = x * (y * z)

to hold (which are the associative laws from math). But for floating point numbers they don't due to rounding errors. Equality tests can also feel, as you see. I know that the original problem is one about parsing, so the underlying representation isn't the same. But if you want to compare floating point numbers for equality it is often better to define a small epsilon constant as something like 1e-06 or such and then compare if |x - y| < epsilon.

For background, there are some good documents on the net about numerics of FP. http://https://floating-point-gui.de/ is one such resource. If you want the deep in-depth treatment I can recommend Knuth's "The Art of Computer Programming Vol 2: Seminumerical Algorithms".


Brian Candler

unread,
Feb 16, 2021, 11:56:22 AM2/16/21
to golang-nuts
On Sunday, 14 February 2021 at 21:57:31 UTC kortschak wrote:
123.4 cannot be represented in binary with a finite number of bits.


Santhosh T

unread,
Feb 16, 2021, 3:30:28 PM2/16/21
to Brian Candler, golang-nuts
in Java, this is not case.

        BigDecimal v = new BigDecimal("123.4");
        System.out.printf("%.20f\n", v); // prints 123.40000000000000000000
        System.out.printf("%.40f\n", v); // prints 123.4000000000000000000000000000000000000000

you can see that it is represented exactly. I thought it was the same with big.Float in golang.

thanks
Santhosh


--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/3t9ao7qtrlM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/721b9ad4-32f0-4aeb-93e6-4fe757174b5dn%40googlegroups.com.

Jan Mercl

unread,
Feb 16, 2021, 3:34:00 PM2/16/21
to Santhosh T, Brian Candler, golang-nuts
On Tue, Feb 16, 2021 at 9:30 PM Santhosh T <santhos...@gmail.com> wrote:

> in Java, this is not case.

That compares decimal vs floating point representations. Those have
very different properties, for different usage patterns.

Brian Candler

unread,
Feb 16, 2021, 3:34:51 PM2/16/21
to golang-nuts
You compared Golang's BigFloat with Java's BigDecimal.  They are not the same.

Santhosh T

unread,
Feb 16, 2021, 4:04:30 PM2/16/21
to Brian Candler, golang-nuts
is there java's BigDecimal equivalent in golang ?

thanks
Santhosh

Dan Kortschak

unread,
Feb 16, 2021, 4:22:20 PM2/16/21
to golan...@googlegroups.com
On Wed, 2021-02-17 at 02:33 +0530, Santhosh T wrote:
> is there java's BigDecimal equivalent in golang ?

There is, for example https://github.com/shopspring/decimal. But you
should ask yourself why you need this.


Brian Candler

unread,
Feb 16, 2021, 4:32:28 PM2/16/21
to golang-nuts
Not in the standard libraries. See (declined):


However there are links to third-party libraries in those tickets.

Santhosh T

unread,
Feb 17, 2021, 1:42:13 AM2/17/21
to Brian Candler, golang-nuts
If it is not in stadnard library, then i will use the approach taken by json.Number in stdlib

that seems simpler, let the users decide how to use it.

thanks
Santhosh

Reply all
Reply to author
Forward
0 new messages