www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - formatting a float or double in a string with all significant digits

reply dan <dan.hitt gmail.com> writes:
I have a double precision number that i would like to print all 
significant digits of, but no more than what are actually present 
in the number.  Or more exactly, i want to print the minimum 
number of digits necessary to recover the original number to 
within 2 or 3 least significant bits in the stored, in-core, 
version of its bit pattern.

For example,


import std.string;
import std.stdio;
import std.math;

void main( ) {
   auto t = format("%3.30f", PI );
   writeln("Value of PI is: ", PI, " or, : ", t);
}

The default way writeln prints is 5 digits to the right of the 
decimal point.

I can format to print with any number of digits, such as 30 
above, but that's too many.

For pi, the correct number of digits to print looks to be about 
18 (and the extra 12 digits presumably are from the decimal 
expansion of the least significant bit?).

But i would like to be able to do this without knowing the 
expansion of pi, or writing too much code, especially if there's 
some d function like writeAllDigits or something similar.

Thanks in advance for any pointers!

dan
Oct 08 2019
next sibling parent reply berni44 <dlang d-ecke.de> writes:
On Tuesday, 8 October 2019 at 20:37:03 UTC, dan wrote:
 But i would like to be able to do this without knowing the 
 expansion of pi, or writing too much code, especially if 
 there's some d function like writeAllDigits or something 
 similar.
You can use the property .dig to get the number of significant digits of a number: writeln(PI.dig); // => 18 You still need to account for the numbers before the dot. If you're happy with scientific notation you can do: auto t = format("%.*e", PI.dig, PI); writeln("PI = ",t); (By the way, you can shortcut that with writefln!"PI = %.*e"(PI.dig, PI);) If you don't want to use scientific notation, you probably need to do some calculations to find out about the number of digits before the dot (you need abs to make it work for negative numbers too): import std.conv: to; auto x = to!int(log10(abs(PI))); writefln!"%*.*f"(x,PI.dig-x,PI);
Oct 08 2019
parent reply Jon Degenhardt <jond noreply.com> writes:
On Wednesday, 9 October 2019 at 05:46:12 UTC, berni44 wrote:
 On Tuesday, 8 October 2019 at 20:37:03 UTC, dan wrote:
 But i would like to be able to do this without knowing the 
 expansion of pi, or writing too much code, especially if 
 there's some d function like writeAllDigits or something 
 similar.
You can use the property .dig to get the number of significant digits of a number: writeln(PI.dig); // => 18 You still need to account for the numbers before the dot. If you're happy with scientific notation you can do: auto t = format("%.*e", PI.dig, PI); writeln("PI = ",t);
Using the '.dig' property is a really nice idea and looks very useful for this. A clarification though - It's the significant digits in the data type, not the value. (PI is 18 because it's a real, not a double.) So: writeln(1.0f.dig, ", ", float.dig); => 6, 6 writeln(1.0.dig, ", ", double.dig); => 15, 15 writeln(1.0L.dig, ", ", real.dig); => 18, 18 Another possibility would be to combine the '.dig' property with the "%g" option, similar to the use "%e" shown. For example, these lines: writeln(format("%0.*g", PI.dig, PI)); writeln(format("%0.*g", double.dig, 1.0)); writeln(format("%0.*g", double.dig, 100.0)); writeln(format("%0.*g", double.dig, 1.00000001)); writeln(format("%0.*g", double.dig, 0.00000001)); produce: 3.14159265358979324 1 100 1.00000001 1e-08 Hopefully experimenting with the different formatting options available will yield one that works for your use case.
Oct 09 2019
parent GreatSam4sure <greatsam4sure gmail.com> writes:
On Wednesday, 9 October 2019 at 07:16:43 UTC, Jon Degenhardt 
wrote:
 On Wednesday, 9 October 2019 at 05:46:12 UTC, berni44 wrote:
 On Tuesday, 8 October 2019 at 20:37:03 UTC, dan wrote:
 But i would like to be able to do this without knowing the 
 expansion of pi, or writing too much code, especially if 
 there's some d function like writeAllDigits or something 
 similar.
You can use the property .dig to get the number of significant digits of a number: writeln(PI.dig); // => 18 You still need to account for the numbers before the dot. If you're happy with scientific notation you can do: auto t = format("%.*e", PI.dig, PI); writeln("PI = ",t);
Using the '.dig' property is a really nice idea and looks very useful for this. A clarification though - It's the significant digits in the data type, not the value. (PI is 18 because it's a real, not a double.) So: writeln(1.0f.dig, ", ", float.dig); => 6, 6 writeln(1.0.dig, ", ", double.dig); => 15, 15 writeln(1.0L.dig, ", ", real.dig); => 18, 18 Another possibility would be to combine the '.dig' property with the "%g" option, similar to the use "%e" shown. For example, these lines: writeln(format("%0.*g", PI.dig, PI)); writeln(format("%0.*g", double.dig, 1.0)); writeln(format("%0.*g", double.dig, 100.0)); writeln(format("%0.*g", double.dig, 1.00000001)); writeln(format("%0.*g", double.dig, 0.00000001)); produce: 3.14159265358979324 1 100 1.00000001 1e-08 Hopefully experimenting with the different formatting options available will yield one that works for your use case.
Good solution But what does it takes to leave a number the way it is without formatting automatic to 6 significant figure out how that requires such a work around as shown above. I know C language is the same but must D follow suit. One of the compilers, i think the DMD 2.084 (not sure now) correct but now the recent one's have revert back. Every number actionscript, javascript, etc. I don't think it will take that much effort for trivial things like to stress developers. This is the best solution i have ever seen on this issue on the forum.i have ask this question several times on this forum
Oct 09 2019
prev sibling parent reply David Briant <dbriant symmetryinvestments.com> writes:
On Tuesday, 8 October 2019 at 20:37:03 UTC, dan wrote:
 I have a double precision number that i would like to print all 
 significant digits of, but no more than what are actually 
 present in the number.  Or more exactly, i want to print the 
 minimum number of digits necessary to recover the original 
 number to within 2 or 3 least significant bits in the stored, 
 in-core, version of its bit pattern.

 For example,


 import std.string;
 import std.stdio;
 import std.math;

 void main( ) {
   auto t = format("%3.30f", PI );
   writeln("Value of PI is: ", PI, " or, : ", t);
 }

 The default way writeln prints is 5 digits to the right of the 
 decimal point.

 I can format to print with any number of digits, such as 30 
 above, but that's too many.

 For pi, the correct number of digits to print looks to be about 
 18 (and the extra 12 digits presumably are from the decimal 
 expansion of the least significant bit?).

 But i would like to be able to do this without knowing the 
 expansion of pi, or writing too much code, especially if 
 there's some d function like writeAllDigits or something 
 similar.

 Thanks in advance for any pointers!

 dan
Hi Dan, What's your usecase here, e.g. a csv/json reader / writer? You say it's for double precision numbers (64bit format) then provide an example for reals (80bit format). So I'm not certain your goal. If you google "what every developer should know about doubles" you'll hit a number of useful articles that explain the common issues of floating point representation in detail. -- David
Oct 09 2019
parent reply dan <dan.hitt gmail.com> writes:
On Wednesday, 9 October 2019 at 10:54:49 UTC, David Briant wrote:
 On Tuesday, 8 October 2019 at 20:37:03 UTC, dan wrote:
 I have a double precision number that i would like to print 
 all significant digits of, but no more than what are actually 
 present in the number.  Or more exactly, i want to print the 
 minimum number of digits necessary to recover the original 
 number to within 2 or 3 least significant bits in the stored, 
 in-core, version of its bit pattern.

 For example,


 import std.string;
 import std.stdio;
 import std.math;

 void main( ) {
   auto t = format("%3.30f", PI );
   writeln("Value of PI is: ", PI, " or, : ", t);
 }

 The default way writeln prints is 5 digits to the right of the 
 decimal point.

 I can format to print with any number of digits, such as 30 
 above, but that's too many.

 For pi, the correct number of digits to print looks to be 
 about 18 (and the extra 12 digits presumably are from the 
 decimal expansion of the least significant bit?).

 But i would like to be able to do this without knowing the 
 expansion of pi, or writing too much code, especially if 
 there's some d function like writeAllDigits or something 
 similar.

 Thanks in advance for any pointers!

 dan
Hi Dan, What's your usecase here, e.g. a csv/json reader / writer? You say it's for double precision numbers (64bit format) then provide an example for reals (80bit format). So I'm not certain your goal. If you google "what every developer should know about doubles" you'll hit a number of useful articles that explain the common issues of floating point representation in detail. -- David
Thanks David for your reply. Thanks also berni44 for the information about the dig attribute, Jon for the neat packaging into one line using the attribute on the type. Unfortunately, the version of gdc that comes with the version of debian that i am using does not have the dig attribute yet, but perhaps i can upgrade, and eventually i think gdc will have it. And thanks GreatSam4sure for your reply --- i searched the archives first, but very poorly :(. But it's easy to believe that i'm not the first person in the history of the world with this issue. Now, my use case is nothing so useful or general as a csv/json reader/writer. I'm just doing some computations, incorrectly i think, and i want to be able to print out the results and feed them to other software. I'm trying to chase down a problem and rule out as many places for error as i can, and it just seemed strange not to be able to get all the digits out in some easy way. But the dig attribute seems to be a big step forward, and for that i am grateful. dan
Oct 10 2019
parent reply Jon Degenhardt <jond noreply.com> writes:
On Thursday, 10 October 2019 at 17:12:25 UTC, dan wrote:
 Thanks also berni44 for the information about the dig attribute,
 Jon for the neat packaging into one line using the attribute on 
 the type.
 Unfortunately, the version of gdc that comes with the version 
 of debian
 that i am using does not have the dig attribute yet, but 
 perhaps i can
 upgrade, and eventually i think gdc will have it.
Glad these ideas helped. The value of the 'double.dig' property is not going to change between compilers/versions/etc. It's really a property of IEEE 754 floating point for 64 bit floats. (D specified the size of double as 64). So, if you are using double, then it's pretty safe to use 15 until the compiler you're using is further along on versions. Declare an enum or const variable to give it a name so you can track it down later. Also, don't get thrown off by the PI is a real, not a double. D supports 80 bit floats as real, so constants like PI are defined as real. But if you convert PI to a double, it'll then have 15 significant bits of precision. --Jon
Oct 10 2019
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Oct 10, 2019 at 09:13:05PM +0000, Jon Degenhardt via
Digitalmars-d-learn wrote:
 On Thursday, 10 October 2019 at 17:12:25 UTC, dan wrote:
 Thanks also berni44 for the information about the dig attribute, Jon
 for the neat packaging into one line using the attribute on the
 type.
 Unfortunately, the version of gdc that comes with the version of
 debian that i am using does not have the dig attribute yet, but
 perhaps i can upgrade, and eventually i think gdc will have it.
What's the output of dmd --version? I find it extremely odd that .dig isn't supported. AFAIK, it's been there since the early days of D. Certainly, it has been there since I started using D, which was before dmd was ever available in Debian. What's the compiler output of: pragma(msg, float.dig); ? T -- Many open minds should be closed for repairs. -- K5 user
Oct 10 2019
parent dan <dan.hitt gmail.com> writes:
On Thursday, 10 October 2019 at 22:44:05 UTC, H. S. Teoh wrote:
 On Thu, Oct 10, 2019 at 09:13:05PM +0000, Jon Degenhardt via 
 Digitalmars-d-learn wrote:
 On Thursday, 10 October 2019 at 17:12:25 UTC, dan wrote:
 Thanks also berni44 for the information about the dig 
 attribute, Jon
 for the neat packaging into one line using the attribute on 
 the
 type.
 Unfortunately, the version of gdc that comes with the 
 version of
 debian that i am using does not have the dig attribute yet, 
 but
 perhaps i can upgrade, and eventually i think gdc will have 
 it.
What's the output of dmd --version? I find it extremely odd that .dig isn't supported. AFAIK, it's been there since the early days of D. Certainly, it has been there since I started using D, which was before dmd was ever available in Debian. What's the compiler output of: pragma(msg, float.dig); ? T
Thanks HS! I sure thought i got a compile time error when i used .dig, but i tried it again, and it works. I tried the pragma, and it printed out 6, and i tried PI.dig and double.dig and they're all working now. Just for reference, i'm using what i think is the standard gdc on debian 9, not dmd, and the version is 2068L. Thanks again for your help in encouraging me to try harder, and thanks again everybody else for all the help. dan
Oct 10 2019