www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.announce - Another take on decimal data types

reply rumbu <rumbu rumbu.ro> writes:
This is my first D finalized project (+16k loc).

I know that there are other two projects intended to provide a 
decimal data type for D, but I consider mine the most complete 
and most compliant to the standards (at least until now).

There are two years of since I'm working on it (and learning D in 
the same time), but I concentrated most of the efforts in the 
last two months.

It was a nice exercise because I was happy to remember the math I 
learn through my college years (trigonometry, logarithms, Taylor 
series, derivatives, etc). Unfortunately I'm not using the same 
math during my day-to day job.

Maybe in another post I will share my struggles I encountered 
during the development (plenty of). But a big thank you goes to 
Rainer Schuetze: without Visual Studio and without the integrated 
debugger this project was impossible to maintain.

Now on topic:

- fully IEEE-754-2008 compliant;
- one flat file;
- using Intel's binary decimal enconding;
- three decimal data types: decimal32, decimal64 and decimal128
- all D operators supported for all numeric types (left and right 
side integrals, floats, chars);
- conversion supported from/to integrals, floats, bools, chars
- conversion to/from other decimal formats (Microsoft Currency, 
Microsoft Decimal, IBM Densely Packed Decimal)
- all std.math functions implemented (even logarithms and 
trigonometry);
- all format specifiers implemented (%f, %e, %g, %a);
- integrated with phobos format and conversion functions (to, 
format, writef);
- thread local precision (from 1 to 34 decimal digits);
- new rounding mode - Europe's most used - tiesToAway;
- alternate exception handling (through flags);
- minimal dependencies (some traits and some floating point 
functions);
- comprehensive documentation;

Source code: 
https://github.com/rumbu13/decimal/blob/master/src/decimal.d

Documentation: http://rumbu13.github.io/decimal/doc/decimal.html

The project is more than in an alpha state, all operations were 
tested but not exhaustively.

What's next:
- more tests;
- benchmarks;
Jan 08 2018
next sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
Great job.

1) Assembly
2) That file needs to be split up. I can feel the lag as I scroll it.
Jan 08 2018
parent reply rumbu <rumbu rumbu.ro> writes:
On Monday, 8 January 2018 at 22:54:06 UTC, rikki cattermole wrote:
 Great job.

 1) Assembly
 2) That file needs to be split up. I can feel the lag as I 
 scroll it.
1) I don't understand :) 2) Done. On Monday, 8 January 2018 at 22:46:27 UTC, Daniel Kozak wrote:
 Wow awesome, it would be nice if you could add it as a dub 
 package (
 http://code.dlang.org/publish) to dub repository 
 (http://code.dlang.org)
Done. Github project: https://github.com/rumbu13/decimal Documentation: http://rumbu13.github.io/decimal/doc/package.html Dub package: https://code.dlang.org/packages/decimal
Jan 09 2018
parent Dennis Cote <private secret.com> writes:
On Tuesday, 9 January 2018 at 22:00:13 UTC, rumbu wrote:
 Documentation: http://rumbu13.github.io/decimal/doc/package.html
I noticed a minor typo in the documentation: auto b = decimal32(123456789); //inexact, represented as 1234568 * x 10^^2 I believe the "*" should be deleted. This is great documentation!
Jan 09 2018
prev sibling next sibling parent Daniel Kozak <kozzi11 gmail.com> writes:
Wow awesome, it would be nice if you could add it as a dub package (
http://code.dlang.org/publish) to dub repository (http://code.dlang.org)

On Mon, Jan 8, 2018 at 11:16 PM, rumbu via Digitalmars-d-announce <
digitalmars-d-announce puremagic.com> wrote:

 This is my first D finalized project (+16k loc).

 I know that there are other two projects intended to provide a decimal
 data type for D, but I consider mine the most complete and most compliant
 to the standards (at least until now).

 There are two years of since I'm working on it (and learning D in the same
 time), but I concentrated most of the efforts in the last two months.

 It was a nice exercise because I was happy to remember the math I learn
 through my college years (trigonometry, logarithms, Taylor series,
 derivatives, etc). Unfortunately I'm not using the same math during my
 day-to day job.

 Maybe in another post I will share my struggles I encountered during the
 development (plenty of). But a big thank you goes to Rainer Schuetze:
 without Visual Studio and without the integrated debugger this project was
 impossible to maintain.

 Now on topic:

 - fully IEEE-754-2008 compliant;
 - one flat file;
 - using Intel's binary decimal enconding;
 - three decimal data types: decimal32, decimal64 and decimal128
 - all D operators supported for all numeric types (left and right side
 integrals, floats, chars);
 - conversion supported from/to integrals, floats, bools, chars
 - conversion to/from other decimal formats (Microsoft Currency, Microsoft
 Decimal, IBM Densely Packed Decimal)
 - all std.math functions implemented (even logarithms and trigonometry);
 - all format specifiers implemented (%f, %e, %g, %a);
 - integrated with phobos format and conversion functions (to, format,
 writef);
 - thread local precision (from 1 to 34 decimal digits);
 - new rounding mode - Europe's most used - tiesToAway;
 - alternate exception handling (through flags);
 - minimal dependencies (some traits and some floating point functions);
 - comprehensive documentation;

 Source code: https://github.com/rumbu13/decimal/blob/master/src/decimal.d

 Documentation: http://rumbu13.github.io/decimal/doc/decimal.html

 The project is more than in an alpha state, all operations were tested but
 not exhaustively.

 What's next:
 - more tests;
 - benchmarks;
Jan 08 2018
prev sibling next sibling parent Bastiaan Veelo <Bastiaan Veelo.net> writes:
On Monday, 8 January 2018 at 22:16:25 UTC, rumbu wrote:
 Documentation: http://rumbu13.github.io/decimal/doc/decimal.html
Wow, slick documentation!
Jan 09 2018
prev sibling next sibling parent reply Mike Franklin <slavo5150 yahoo.com> writes:
On Monday, 8 January 2018 at 22:16:25 UTC, rumbu wrote:
 This is my first D finalized project (+16k loc).

 I know that there are other two projects intended to provide a 
 decimal data type for D, but I consider mine the most complete 
 and most compliant to the standards (at least until now).
Wow!. So thorough. Documentation is fantastic. Very nice work, indeed! Mike
Jan 09 2018
parent aberba <karabutaworld gmail.com> writes:
On Tuesday, 9 January 2018 at 10:41:42 UTC, Mike Franklin wrote:
 On Monday, 8 January 2018 at 22:16:25 UTC, rumbu wrote:
 This is my first D finalized project (+16k loc).

 I know that there are other two projects intended to provide a 
 decimal data type for D, but I consider mine the most complete 
 and most compliant to the standards (at least until now).
Wow!. So thorough. Documentation is fantastic. Very nice work, indeed! Mike
I couldn't say it better. Its really well done...
Jan 09 2018
prev sibling next sibling parent reply =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
On 2018-01-08 22:16:25 +0000, rumbu said:

 This is my first D finalized project (+16k loc).
Great stuff! Will this work in betterC mode? Side-Note: I'm always missing the betterC information or is the philosophy to just try it out? -- Robert M. Mnch http://www.saphirion.com smarter | better | faster
Jan 11 2018
next sibling parent reply Seb <seb wilzba.ch> writes:
On Thursday, 11 January 2018 at 11:19:41 UTC, Robert M. Münch 
wrote:
 On 2018-01-08 22:16:25 +0000, rumbu said:

 This is my first D finalized project (+16k loc).
Great stuff! Will this work in betterC mode? Side-Note: I'm always missing the betterC information or is the philosophy to just try it out?
We have now this page: https://dlang.org/spec/betterc.html Well, betterC it's still a WIP feature and its feature subset is improved from release to release. In doubt you can always use run.dlang.io to quickly try out things.
Jan 11 2018
parent =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
On 2018-01-11 15:34:04 +0000, Seb said:

 Side-Note: I'm always missing the betterC information or is the 
 philosophy to just try it out?
We have now this page: https://dlang.org/spec/betterc.html
Hi, thanks. Should have been more precise: An informaiton for every lib if it is compatible with betterC. IMO betterC is the strategic trojan horse to get D into the broader masses.
 Well, betterC it's still a WIP feature and its feature subset is 
 improved from release to release.
 In doubt you can always use run.dlang.io to quickly try out things.
That's great, thanks for the hint. -- Robert M. Mnch http://www.saphirion.com smarter | better | faster
Jan 11 2018
prev sibling parent reply rumbu <rumbu rumbu.ro> writes:
On Thursday, 11 January 2018 at 11:19:41 UTC, Robert M. Münch 
wrote:
 On 2018-01-08 22:16:25 +0000, rumbu said:

 This is my first D finalized project (+16k loc).
Great stuff! Will this work in betterC mode?
It will not work without some refactory. Most of phobos/druntime dependencies are minimal an can be rewritten - in fact there are only 9 dependencies: bsr, bsf, addu, subu, adds, subs from druntime and isNaN, isinfinity, signbit from std.math. The rest of the dependencies are simply traits that must work by default under betterC. Once dependencies are solved, another problem will be the exception mechanism. 90% of arithmetic operations are meant to throw exceptions, but this can be overridden by the alternate exception handling - raising and setting flags. BUT - a very big but - the most important issue is the formatting thing. Even there is no direct dependency on std.format or std.stdio, the formatting mechanism is designed to fit nicely into phobos formatting paradigm. That means that there is no way to output decimal values on the console without phobos. I confess that 30% of my development time was the formatting feature: displaying correctly any possible combination of width, padding, alignment in 4 different formats (%f, %g, %e, %a) was really a challenge and despite of hundreds of unit tests, I'm not convinced today that I covered all the corner cases. The f*cking %g took me 3 days to find out exactly what is meant to do. More than that, each compiler producer have a different idea about format specifiers. Linking your application against snn, msvcrt or glibc will render completely different results for printf("%g", ...). On top of that, neither the FormatSpec documentation from phobos is something complete and clear... AFAIK, there is no way to plug a custom formatting mechanism into printf, not even in plain C (ok, I'm aware that gcc has something in this respect). To sum things up, it is possible to use it in betterC mode if: - all dependencies are rewritten; - the exception handling is dropped out completely; - a printf equivalent for decimal types is rewritten from scratch or - decimal values must be converted to binary float before printing them - but this will negate the main purpose of the decimal type - precision.
Jan 11 2018
next sibling parent jmh530 <john.michael.hall gmail.com> writes:
On Thursday, 11 January 2018 at 18:15:23 UTC, rumbu wrote:
 [snip]
 BUT - a very big but - the most important issue is the 
 formatting thing. Even there is no direct dependency on 
 std.format or std.stdio, the formatting mechanism is designed 
 to fit nicely into phobos formatting paradigm. That means that 
 there is no way to output decimal values on the console without 
 phobos. I confess that 30% of my development time was the 
 formatting feature: displaying correctly any possible 
 combination of width, padding, alignment in 4 different formats 
 (%f, %g, %e, %a) was really a challenge and despite of hundreds 
 of unit tests, I'm not convinced today that I covered all the 
 corner cases. The f*cking %g took me 3 days to find out exactly 
 what is meant to do. More than that, each compiler producer 
 have a different idea about format specifiers. Linking your 
 application against snn, msvcrt or glibc will render completely 
 different results for printf("%g", ...). On top of that, 
 neither the FormatSpec documentation from phobos is something 
 complete and clear...
Ilya has expressed interest in a betterC formatting library for libmir.
Jan 11 2018
prev sibling next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 1/11/18 1:15 PM, rumbu wrote:
 On Thursday, 11 January 2018 at 11:19:41 UTC, Robert M. Münch wrote:
 On 2018-01-08 22:16:25 +0000, rumbu said:

 This is my first D finalized project (+16k loc).
Great stuff! Will this work in betterC mode?
It will not work without some refactory. Most of phobos/druntime dependencies are minimal an can be rewritten - in fact there are only 9 dependencies: bsr, bsf, addu, subu, adds, subs from druntime and isNaN, isinfinity, signbit from std.math.
Some of those are intrinsics, and don't actually link to any druntime functions. -Steve
Jan 11 2018
prev sibling parent =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
On 2018-01-11 18:15:23 +0000, rumbu said:

 On Thursday, 11 January 2018 at 11:19:41 UTC, Robert M. Mnch wrote:
 
 Great stuff! Will this work in betterC mode?
It will not work without some refactory. Most of phobos/druntime dependencies are minimal an can be rewritten - in fact there are only 9 dependencies: bsr, bsf, addu, subu, adds, subs from druntime and isNaN, isinfinity, signbit from std.math. The rest of the dependencies are simply traits that must work by default under betterC.
Ok, that doesn't sound impossible.
 Once dependencies are solved, another problem will be the exception 
 mechanism. 90% of arithmetic operations are meant to throw exceptions, 
 but this can be overridden by the alternate exception handling - 
 raising and setting flags.
Yes, makes sense.
 BUT - a very big but - the most important issue is the formatting thing.
 ...
 a printf equivalent for decimal types is rewritten from scratch or - 
 decimal values must be converted to binary float before printing them - 
 but this will negate the main purpose of the decimal type - precision.
Well, I think even using a decimal128 for some calculations to have higher precision intermediate results and only converting them to float for UI interaction would make a lot of sense. At least in our use-case, that would be quite interesting. Looking forward to see some benchmarks. -- Robert M. Mnch http://www.saphirion.com smarter | better | faster
Jan 11 2018
prev sibling next sibling parent reply kdevel <kdevel vogtner.de> writes:
Great project!

On Monday, 8 January 2018 at 22:16:25 UTC, rumbu wrote:
 - all format specifiers implemented (%f, %e, %g, %a);
Really? [...]
 What's next:
 - more tests;
Here you are: ``` import std.stdio; import decimal; void main () { decimal32 d = "0.7"; d *= decimal32("1.05"); d.writeln; printf ("%.2f\n", d); float f = 0.7f; f *= 1.05f; f.writeln; printf ("%.2f\n", f); decimal32 e = 1_000_000_000; while (e > 1e-7) { e.writeln; e /= 10; } } ``` This prints: 0.735 0.00 <--- expected: 0.74 0.735 0.73 <--- loop output missing (DMD64 D Compiler v2.077.1)
Jan 11 2018
next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On Thursday, 11 January 2018 at 20:35:03 UTC, kdevel wrote:
 Great project!

 On Monday, 8 January 2018 at 22:16:25 UTC, rumbu wrote:
 - all format specifiers implemented (%f, %e, %g, %a);
Really? [...]
 What's next:
 - more tests;
Here you are: ``` import std.stdio; import decimal; void main () { decimal32 d = "0.7"; d *= decimal32("1.05"); d.writeln; printf ("%.2f\n", d);
C’s printf by definition can’t be customized. What did you expect?
    float f = 0.7f;
    f *= 1.05f;
    f.writeln;
    printf ("%.2f\n", f);

    decimal32 e = 1_000_000_000;
    while (e > 1e-7) {
       e.writeln;
       e /= 10;
    }
 }
 ```

 This prints:

    0.735
    0.00      <--- expected: 0.74
    0.735
    0.73
              <--- loop output missing

 (DMD64 D Compiler v2.077.1)
Jan 11 2018
parent reply kdevel <kdevel vogtner.de> writes:
On Thursday, 11 January 2018 at 20:40:01 UTC, Dmitry Olshansky 
wrote:
    printf ("%.2f\n", d);
C’s printf by definition can’t be customized.
Sure. http://www.gnu.org/software/libc/manual/html_node/Customizing-Printf.html
 What did you expect?
To be honest: A compile time error. Modern C compilers can check such format strings. Example: GCC 6: mis.c ``` #include <stdio.h> int main () { double d = 0; printf ("%p\n", d); return 0; } ``` $ gcc -Wall mis.c mis.c: In function 'main': mis.c:6:14: warning: format '%p' expects argument of type 'void *', but argument 2 has type 'double' [-Wformat=] printf ("%p\n", d);
Jan 11 2018
next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 1/11/18 4:12 PM, kdevel wrote:
 On Thursday, 11 January 2018 at 20:40:01 UTC, Dmitry Olshansky wrote:
 What did you expect?
To be honest: A compile time error. Modern C compilers can check such format strings. Example: GCC 6:
But dmd isn't a C compiler, nor does it have to worry about the problems C has (namely, untyped varargs). To dmd, printf is just another function, there's nothing special about it. -Steve
Jan 11 2018
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Jan 11, 2018 at 04:38:57PM -0500, Steven Schveighoffer via
Digitalmars-d-announce wrote:
 On 1/11/18 4:12 PM, kdevel wrote:
 On Thursday, 11 January 2018 at 20:40:01 UTC, Dmitry Olshansky wrote:
 What did you expect?
To be honest: A compile time error. Modern C compilers can check such format strings. Example: GCC 6:
But dmd isn't a C compiler, nor does it have to worry about the problems C has (namely, untyped varargs). To dmd, printf is just another function, there's nothing special about it.
[...] Yeah, checking C-style printf formats isn't dmd's problem. The Phobos equivalent of printf, however, *does* support compile-time format checking in the latest version: writefln!"%s %d %d"("abc", 1); // "Orphan format specifier: %d" writefln!"%s %d"("abc", 1, 2); // "Orphan format arguments: args[2..3]" writefln!"%s %d"(1, "abc"); // "Incorrect format specifier for range: %d" writefln!"%f"(1); // "incompatible format character for integral argument: %f" Best of all, this is all done via CTFE in the library code, no hard-coding in the compiler necessary. You can implement your own compile-time checker for your own DSLs in the same way, without needing to hack the compiler. T -- MSDOS = MicroSoft's Denial Of Service
Jan 11 2018
parent Joakim <dlang joakim.fea.st> writes:
On Thursday, 11 January 2018 at 22:07:42 UTC, H. S. Teoh wrote:
 On Thu, Jan 11, 2018 at 04:38:57PM -0500, Steven Schveighoffer 
 via Digitalmars-d-announce wrote:
 On 1/11/18 4:12 PM, kdevel wrote:
 On Thursday, 11 January 2018 at 20:40:01 UTC, Dmitry 
 Olshansky wrote:
 What did you expect?
To be honest: A compile time error. Modern C compilers can check such format strings. Example: GCC 6:
But dmd isn't a C compiler, nor does it have to worry about the problems C has (namely, untyped varargs). To dmd, printf is just another function, there's nothing special about it.
[...] Yeah, checking C-style printf formats isn't dmd's problem. The Phobos equivalent of printf, however, *does* support compile-time format checking in the latest version: writefln!"%s %d %d"("abc", 1); // "Orphan format specifier: %d" writefln!"%s %d"("abc", 1, 2); // "Orphan format arguments: args[2..3]" writefln!"%s %d"(1, "abc"); // "Incorrect format specifier for range: %d" writefln!"%f"(1); // "incompatible format character for integral argument: %f" Best of all, this is all done via CTFE in the library code, no hard-coding in the compiler necessary. You can implement your own compile-time checker for your own DSLs in the same way, without needing to hack the compiler.
Interesting, guess this was added last April with dmd 2.074? https://dlang.org/changelog/2.074.0.html#std-format-formattedWrite You or someone should write up a blog post about this, with both this example and expanding on how D enables this kind of functionality in general.
Jan 12 2018
prev sibling parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On Thursday, 11 January 2018 at 21:12:59 UTC, kdevel wrote:
 On Thursday, 11 January 2018 at 20:40:01 UTC, Dmitry Olshansky 
 wrote:
    printf ("%.2f\n", d);
C’s printf by definition can’t be customized.
Sure.
“”” GNU C Library lets you define “”” Here is your compatibility story.
 What did you expect?
To be honest: A compile time error. Modern C compilers can check such format strings. Example: GCC 6:
1. It’s a warning. 2. There is no typechecking it’s just a hardcoded linting in the compiler. 3. It’s D use writefln or if going for C primitives know their quirks. 4. Passing anything but basic types to C vararg is undefined in C++ IIRC, dunno what D should do about it.
 mis.c
 ```
 #include <stdio.h>

 int main ()
 {
    double d = 0;
    printf ("%p\n", d);
    return 0;
 }
 ```

 $ gcc -Wall mis.c
 mis.c: In function 'main':
 mis.c:6:14: warning: format '%p' expects argument of type 'void 
 *', but argument 2 has type 'double' [-Wformat=]
     printf ("%p\n", d);
Jan 11 2018
prev sibling parent reply kdevel <kdevel vogtner.de> writes:
On Thursday, 11 January 2018 at 20:35:03 UTC, kdevel wrote:
    printf ("%.2f\n", d);
This must read writefln ("%.2f", d); Would have expected a compile time/run time error.
 This prints:

    0.735
    0.00      <--- expected: 0.74
Good. writefln prints the expected value.
    0.735
    0.73
              <--- loop output missing
This is still missing. If I change the while-loop threshold to 1e-9 the values are printed.
Jan 11 2018
parent reply kdevel <kdevel vogtner.de> writes:
On Thursday, 11 January 2018 at 20:44:13 UTC, kdevel wrote:
 On Thursday, 11 January 2018 at 20:35:03 UTC, kdevel wrote:
              <--- loop output missing
loop.d ``` import std.stdio; import decimal; void loopme(T) () { "---".writeln; T e = T(1000); while (e > T(1e-6)) { e.writeln; e /= 10; } } void main () { loopme!float; loopme!decimal32; } ``` This prints --- 1000 100 10 1 0.1 0.01 0.001 0.0001 1e-05 --- 1000 100 10 1 0.1 0.0100000 <-- 0.00100000 <-- 0.000100000 <-- 1.00000e-05 <-- Why are there trailing zeroes?
Jan 11 2018
parent reply rumbu <rumbu rumbu.ro> writes:
On Thursday, 11 January 2018 at 21:24:30 UTC, kdevel wrote:
 On Thursday, 11 January 2018 at 20:44:13 UTC, kdevel wrote:
 On Thursday, 11 January 2018 at 20:35:03 UTC, kdevel wrote:
              <--- loop output missing
loop.d ``` import std.stdio; import decimal; void loopme(T) () { "---".writeln; T e = T(1000); while (e > T(1e-6)) { e.writeln; e /= 10; } } void main () { loopme!float; loopme!decimal32; } ``` This prints --- 1000 100 10 1 0.1 0.01 0.001 0.0001 1e-05 --- 1000 100 10 1 0.1 0.0100000 <-- 0.00100000 <-- 0.000100000 <-- 1.00000e-05 <-- Why are there trailing zeroes?
As I said, the %g specifier (used by default in writeln) makes me cry. Anyway, I made some modifications, now it prints correctly (in fact using floats just proves the need of decimals on my system): -- 1000 100 10 1 0.1 0.01 0.00099999 0.000099999 1e-05 --- 1000 100 10 1 0.1 0.01 0.001 0.0001 1e-05 Regarding printf, I cannot help, this is a C function, has nothing to do with D formatting.
Jan 11 2018
parent reply kdevel <kdevel vogtner.de> writes:
On Thursday, 11 January 2018 at 22:36:40 UTC, rumbu wrote:
 1000
 100
 10
 1
 0.1
 0.01
 0.001
 0.0001
 1e-05

 Regarding printf, I cannot help, this is a C function, has 
 nothing to do with D formatting.
Sure. What about the failed comparison: gt.d ``` import std.stdio; import decimal; void loopme(T) () { "---".writeln; T e = 10; while (e > 1e-6) { e /= 10; writeln (e, ' ', e > 1e-6); } } void main () { loopme!decimal32; loopme!decimal64; loopme!decimal128; } ``` This gives here: --- 1 true 0.1 false --- 1 true 0.1 false --- 1 true 0.1 true 0.0100000 true 0.00100000 true 0.000100000 true 1.00000e-05 true 1.00000e-06 true 1.00000e-07 false
Jan 11 2018
parent reply rumbu <rumbu rumbu.ro> writes:
On Thursday, 11 January 2018 at 23:57:29 UTC, kdevel wrote:
What about the failed comparison:
 gt.d
 ```
 import std.stdio;
 import decimal;

 void loopme(T) ()
 {
    "---".writeln;
    T e = 10;
    while (e > 1e-6) {
       e /= 10;
       writeln (e, ' ', e > 1e-6);
    }
 }

 void main ()
 {
    loopme!decimal32;
    loopme!decimal64;
    loopme!decimal128;
 }
 ```

 This gives here:

    ---
    1 true
    0.1 false
    ---
    1 true
    0.1 false
    ---
    1 true
    0.1 true
    0.0100000 true
    0.00100000 true
    0.000100000 true
    1.00000e-05 true
    1.00000e-06 true
    1.00000e-07 false
This is not failed comparison. 1e-6 cannnot be represented exactly as binary floating point, it is in fact 0.00000099999999999999995481 as double which happens to be less than 1e-6. decimal32 and decimal64 are rounding up the value, decimal128 has enough precision (34 digits) to render exactly the value above. ----- 7 digits: 0.0000010 15 digits: 0.000001000000000 34 digits: 0.0000009999999999999999548100000000
Jan 11 2018
parent reply rumbu <rumbu rumbu.ro> writes:
 On Thursday, 11 January 2018 at 23:57:29 UTC, kdevel wrote:
 What about the failed comparison:

.... You are right in fact, there is also a failed comparison. Now corrected.
Jan 11 2018
parent kdevel <kdevel vogtner.de> writes:
On Friday, 12 January 2018 at 05:18:15 UTC, rumbu wrote:
 On Thursday, 11 January 2018 at 23:57:29 UTC, kdevel wrote:
 What about the failed comparison:

.... You are right in fact, there is also a failed comparison. Now corrected.
Works. Thanks for the changes!
Jan 12 2018
prev sibling next sibling parent reply kdevel <kdevel vogtner.de> writes:
On Monday, 8 January 2018 at 22:16:25 UTC, rumbu wrote:
 - all std.math functions implemented (even logarithms and 
 trigonometry);
nosine.d ``` import std.stdio; // import std.math; import decimal; void nosine (T) () { T d = T(1.1); writeln (sin(d)); } void main () { nosine!decimal32; nosine!decimal64; nosine!decimal128; } ``` $ dmd nosine.d decimal.git/libdecimal.a decimal/package.d(10505): Error: undefined identifier decimalCapAngle decimal/package.d(5364): Error: template instance decimal.decimalSin!(Decimal!32) error instantiating nosine.d(8): instantiated from here: sin!(Decimal!32) nosine.d(13): instantiated from here: nosine!(Decimal!32) decimal/package.d(10505): Error: undefined identifier decimalCapAngle decimal/package.d(5364): Error: template instance decimal.decimalSin!(Decimal!64) error instantiating nosine.d(8): instantiated from here: sin!(Decimal!64) nosine.d(14): instantiated from here: nosine!(Decimal!64) decimal/package.d(10505): Error: undefined identifier decimalCapAngle decimal/package.d(5364): Error: template instance decimal.decimalSin!(Decimal!128) error instantiating nosine.d(8): instantiated from here: sin!(Decimal!128) nosine.d(15): instantiated from here: nosine!(Decimal!128)
Jan 12 2018
parent reply rumbu <rumbu rumbu.ro> writes:
On Friday, 12 January 2018 at 13:09:42 UTC, kdevel wrote:
 $ dmd nosine.d decimal.git/libdecimal.a
 decimal/package.d(10505): Error: undefined identifier 
 decimalCapAngle
Sorry, broke some code when I made the split. Now it's working.
Jan 12 2018
parent reply kdevel <kdevel vogtner.de> writes:
On Saturday, 13 January 2018 at 01:30:12 UTC, rumbu wrote:
 On Friday, 12 January 2018 at 13:09:42 UTC, kdevel wrote:
 $ dmd nosine.d decimal.git/libdecimal.a
 decimal/package.d(10505): Error: undefined identifier 
 decimalCapAngle
Sorry, broke some code when I made the split. Now it's working.
$ dub Performing "debug" build using dmd for x86_64. decimal ~master: building configuration "library"... src/test/test.d(4,5): Error: only one main allowed. Previously found main at src/benchmark/benchmark.d(143,5) dmd failed with exit code 1.
Jan 13 2018
parent reply kdevel <kdevel vogtner.de> writes:
On Saturday, 13 January 2018 at 13:44:20 UTC, kdevel wrote:
 $ dub
 Performing "debug" build using dmd for x86_64.
 decimal ~master: building configuration "library"...
 src/test/test.d(4,5): Error: only one main allowed. Previously 
 found main at src/benchmark/benchmark.d(143,5)
 dmd failed with exit code 1.
diff --git a/dub.json b/dub.json index c48899f..d8882c1 100644 --- a/dub.json +++ b/dub.json -10,16 +10,16 "configurations": [ { "name": "library", - "excludedSourceFiles": [ "src/benchmark.d", "src/test.d" ] + "excludedSourceFiles": [ "src/benchmark/benchmark.d", "src/test/test.d" ] }, { "name": "unittest", - "excludedSourceFiles": [ "src/benchmark.d", "src/test.d" ] + "excludedSourceFiles": [ "src/benchmark/benchmark.d", "src/test/test.d" ] }, { "name": "benchmark", "targetType": "executable", - "excludedSourceFiles": [ "src/test.d" ] + "excludedSourceFiles": [ "src/test/test.d" ] } ] }
Jan 13 2018
parent reply kdevel <kdevel vogtner.de> writes:
On Saturday, 13 January 2018 at 13:49:59 UTC, kdevel wrote:
 diff --git a/dub.json b/dub.json
 index c48899f..d8882c1 100644
 --- a/dub.json
 +++ b/dub.json
    -10,16 +10,16   
      "configurations": [
          {
              "name": "library",
 -            "excludedSourceFiles": [ "src/benchmark.d", 
 "src/test.d" ]
 +            "excludedSourceFiles": [ 
 "src/benchmark/benchmark.d", "src/test/test.d" ]
          },
Now my code does no longer compiles against the decimal package. This is my directory structure: dlang-decimal/ decimal -> decimal.git/src decimal.git [created by git clone <url> decimal.git] nosine.d $ dmd nosine decimal.git/libdecimal.a nosine.d(3): Error: module decimal is in file 'decimal.d' which cannot be read import path[0] = /.../dmd2/linux/bin64/../../src/phobos import path[1] = /.../dmd2/linux/bin64/../../src/druntime/import make: *** [nosine] Fehler 1
Jan 13 2018
parent reply rumbu <rumbu rumbu.ro> writes:
On Saturday, 13 January 2018 at 13:56:20 UTC, kdevel wrote:
 Now my code does no longer compiles against the decimal 
 package. This is my directory structure:
I received a suggestion to reorganize the file structure because of some bug in dub (https://issues.dlang.org/show_bug.cgi?id=11847). The dub.json remained out of sync. I changed it, but I am not 100% sure that it's working. I am not experienced with dub, If someone wants to maintain dub.json, I will be more than happy to accept any pull request. Personally I hate dub because it's polluting my %APPDATA% folder and each time I connect my laptop to the company domain network, I must wait to sync zillions of files.
Jan 13 2018
next sibling parent mark_mcs <mark mnetcs.com> writes:
On Saturday, 13 January 2018 at 14:43:53 UTC, rumbu wrote:
 Personally I hate dub because it's polluting my %APPDATA% 
 folder and each time I connect my laptop to the company domain 
 network, I must wait to sync zillions of files.
Dub uses the roaming profile directory for its cache storage. This needs to be changed to the local profile directory. I'll submit a PR for this at some point in the near future. I have the dub build working locally and will be submitting another PR for that shortly. Unless anyone has any valid complaints, I'd like to convert dub.json to SDL format so that I can add comments without trying to work around the JSON syntax limitations.
Jan 13 2018
prev sibling next sibling parent kdevel <kdevel vogtner.de> writes:
On Saturday, 13 January 2018 at 14:43:53 UTC, rumbu wrote:
 On Saturday, 13 January 2018 at 13:56:20 UTC, kdevel wrote:
 Now my code does no longer compiles against the decimal 
 package. This is my directory structure:
I received a suggestion to reorganize the file structure because of some bug in dub (https://issues.dlang.org/show_bug.cgi?id=11847). The dub.json remained out of sync. I changed it, but I am not 100% sure that it's working. I am not experienced with dub, If someone wants to maintain dub.json, I will be more than happy to accept any pull request.
I only invoked dub in order to generate libdecimal.a
 Personally I hate dub because it's polluting my %APPDATA% 
 folder and each time I connect my laptop to the company domain 
 network, I must wait to sync zillions of files.
I have now the following directory structure: dlang-decimal/ decimal -> decimal.git/src/decimal/ decimal.git/ [git cloned <url> decimal.git] libdecimal.a src/ decimal/ floats.d floats.di integrals.d integrals.di package.d package.di ranges.d ranges.di sinks.d sinks.di nosine.d I have generated the .di files with dmd -H *.d within the src/decimal directory. Now dmd in unwilling to compile: $ dmd nosine.d decimal.git/libdecimal.a decimal/integrals.di(537): Error: xadd cannot be interpreted at compile time, because it has no available source code decimal/integrals.di(567): called from here: xadd(result, z) decimal/integrals.di(121): called from here: fma(this, 10u, cast(uint)(cast(int)c - 48), ovf) decimal/integrals.di(978): called from here: unsigned(0LU, 0LU).this("100000000000000000000") [the four lines repeat untill 77 lines are printed] How come?
Jan 13 2018
prev sibling parent reply kdevel <kdevel vogtner.de> writes:
On Saturday, 13 January 2018 at 14:43:53 UTC, rumbu wrote:
 I received a suggestion to reorganize the file structure 
 because of some bug in dub 
 (https://issues.dlang.org/show_bug.cgi?id=11847). The dub.json 
 remained out of sync.

 I changed it, but I am not 100% sure that it's working. I am 
 not experienced with dub, If someone wants to maintain 
 dub.json, I will be more than happy to accept any pull request.
I can compile/run the nosine.d now. But implicit conversion between decimalX and float/double/real does not seem to work. I came across this in sinus.d ``` import std.stdio; import std.math; import decimal; void main () { real r; for (r = 1; r < 6; r += .1L) { decimal128 d = r; auto dsin = sin (d); auto rsin = sin (r); real delta = dsin - rsin; writefln ("%9.2f %30.24f %12.4g", r, rsin, dsin, delta); } } ``` $ dmd sinus.d decimal.git/libdecimal.a sinus.d(12): Error: cannot implicitly convert expression dsin.opBinary(rsin) of type Decimal!128 to real I tried to change the subtraction to ``` real delta = dsin; delta -= rsin; ``` getting sinus_e1.d(12): Error: cannot implicitly convert expression dsin of type Decimal!128 to real Changes this into ``` real delta = dsin.to!real; delta -= rsin; ``` sinus_e2.d(12): Error: template decimal.to cannot deduce function from argument types !(real)(Decimal!128), candidates are: decimal/package.d(5814): decimal.to(T, D)(auto ref const D x, const RoundingMode mode) if (isIntegral!T && isDecimal!D) decimal/package.d(5832): decimal.to(F, D)(auto ref const D x, const RoundingMode mode) if (isFloatingPoint!F && isDecimal!D) to no avail. Also ``` real delta = cast(real) dsin; ``` did not succeed: decimal/package.d(933): Error: undefined identifier mode, did you mean template modf(D)(auto ref const D x, ref D y) if (isDecimal!D)? sinus_e3.d(12): Error: template instance decimal.Decimal!128.Decimal.opCast!real error instantiating Also ``` real delta = dsin.to!(real, RoundingMode.tiesToEven); ``` ain't work: sinus_e4.d(12): Error: template decimal.to cannot deduce function from argument types !(real, cast(RoundingMode)0)(Decimal!128), candidates are: decimal/package.d(5814): decimal.to(T, D)(auto ref const D x, const RoundingMode mode) if (isIntegral!T && isDecimal!D) decimal/package.d(5832): decimal.to(F, D)(auto ref const D x, const RoundingMode mode) if (isFloatingPoint!F && isDecimal!D) How can I convert a decimalX to float/double/real?
Jan 13 2018
parent reply rumbu <rumbu rumbu.ro> writes:
On Saturday, 13 January 2018 at 17:19:31 UTC, kdevel wrote:

 How can I convert a decimalX to float/double/real?
Implicit conversion is not available at library level, D does not have an implicit conversion operator; Subtracting decimalX - real will result in a decimalX. This was a design decision, but I'm open to suggestions. The following code works: real r; for (r = 1; r < 6; r += .1L) { decimal128 d = r; auto dsin = sin (d); auto rsin = sin (r); auto delta = dsin - rsin; //delta is decimal128 writefln ("%9.2f %30.24f %30.24f %12.4g", r, rsin, dsin, delta); } if you really need to convert decimalX values to floating point counterparts, you'll need to cast them: x = cast(real)somedecimalvalue; (but update your files before this, I discovered a bug in opCast)
Jan 13 2018
parent reply kdevel <kdevel vogtner.de> writes:
On Saturday, 13 January 2018 at 17:46:15 UTC, rumbu wrote:
 The following code works:

     real r;
     for (r = 1; r < 6; r += .1L) {
         decimal128 d = r;
         auto dsin = sin (d);
         auto rsin = sin (r);
         auto delta = dsin - rsin; //delta is decimal128
         writefln ("%9.2f %30.24f %30.24f %12.4g", r, rsin, 
 dsin, delta);
     }
rsinus.d ``` import std.stdio; import std.math; import decimal; void main () { real r; for (r = 1; r < 6; r += .1L) { decimal128 d = r; auto dsin = sin (d); auto rsin = sin (r); auto delta = dsin - rsin; writefln ("%9.2f %30.24f %30.24f %12.4g", r, rsin, dsin, delta); } } ``` I get large numerical dicrepancies and an exception: 1.00 0.841470984807896506664591 0.841470984807896506632968 3.633e-18 1.10 0.891207360061435339970703 0.878166666666666700439167 -1e-02 1.20 0.932039085967226349689098 0.932735999999999982229600 1e-04 1.30 0.963558185417192964729825 0.964774416666666678037840 1e-03 1.40 0.985449729988460180693261 0.985449729988460165022208 2.352e-17 1.50 0.997494986604054430972058 0.997494986604054430940101 -1.206e-17 1.60 0.999573603041505164359688 0.917333333333333309393333 -1e-02 1.70 0.991664810452468615338453 0.991664810452468621659666 5.316e-17 1.80 0.973847630878195186514452 0.973847630878195177217801 3.222e-17 1.90 0.946300087687414488452770 0.946300087687414518558711 5.006e-17 2.00 0.909297426825681695322298 0.909297426825681695408286 -1.229e-17 2.10 0.863209366648873770600393 0.863209366648873727768592 -9.243e-17 2.20 0.808496403819590184279108 0.808496403819590083669388 -1.180e-16 2.30 0.745705212176720177432929 0.745705212176720299976718 1.573e-16 2.40 0.675463180551150926659871 0.675463180551150998081025 4.488e-17 2.50 0.598472144103956494242628 0.598472144103956494052117 -5.365e-17 2.60 0.515501371821464235509908 0.515501371821464164136844 -1.059e-16 2.70 0.427379880233829934906920 0.427379880233829779962563 -1.640e-16 2.80 0.334988150155904919986691 0.334988150155905092912699 1.534e-16 2.90 0.239249329213982328722001 0.239249329213982423337949 8.154e-17 3.00 0.141120008059867222739193 0.141120008059867222100109 9.000e-18 3.10 0.041580662433290579926783 0.041580662433290496264607 -8.502e-17 3.20 -0.058374143427579908318645 -0.058374143427580079845578 -1.746e-16 3.30 -0.157745694143248381115198 -0.157745694143248199327762 1.774e-16 3.40 -0.255541102026831318288136 -0.255541102026831224503296 1.098e-16 3.50 -0.350783227689619847116916 -0.350783227689619848119312 -1.252e-17 3.60 -0.442520443294852383234834 -0.442520443294852457798085 -5.310e-17 3.70 -0.529836140908493212172466 -0.529836140908493358228498 -1.787e-16 3.80 -0.611857890942719074699619 -0.611857890942718929385788 9.811e-17 3.90 -0.687766159183973817053612 -0.687766159183973746224122 2.078e-17 4.00 -0.756802495307928250372134 -0.756802495307928251373997 -5.067e-17 4.10 -0.818277111064410503490831 -0.818277111064410297331800 1.797e-16 4.20 -0.871575772413588059281658 -0.871575772413588143861425 -6.686e-17 4.30 -0.916165936749454983402223 -0.916165936749454908701153 1.013e-16 4.40 -0.951602073889515953533484 -0.951602073889516059449964 -1.634e-16 4.50 -0.977530117665097055001684 -0.977530117665097055387688 -4.889e-17 4.60 -0.993691003633464455912887 -0.993691003633464414974720 -8.475e-18 4.70 -0.999923257564100884174467 -0.999923257564100886240565 -5.624e-17 4.80 -0.996164608835840671935500 -0.996164608835840688651004 -8.651e-18 4.90 -0.982452612624332512691275 -0.982452612624332448489733 2.651e-17 5.00 -0.958924274663138469525139 -0.958924274663138468894495 -1.739e-17 5.10 -0.925814682327732297803524 -0.925814682327732436044974 -9.704e-17 5.20 -0.883454655720153265790421 -0.883454655720153186437488 5.926e-17 5.30 -0.832267442223901164892897 -0.832267442223901270573400 -1.281e-16 5.40 -0.772764487555987363974919 -0.772764487555987145324893 2.202e-16 5.50 -0.705540325570391908089566 -0.705540325570391906230803 1.647e-17 5.60 -0.631266637872321313522395 -0.631266637872321596869491 -3.079e-16 5.70 -0.550685542597637763537807 -25.165500000000002545915000 - core.exception.RangeError decimal/package.d(6652): Range violation ---------------- ??:? _d_arrayboundsp [0x46ccea] decimal/package.d:6652 void decimal.sinkFloat!(char, decimal.integrals.unsigned!(128).unsigned).sinkFloat(ref const(std.format.FormatSpec!(char).FormatSpec), void delegate(const(char)[]), const(decimal.integrals.unsigned!(128).unsigned), const(int), const(bool), const(decimal.RoundingMode), const(bool)) [0x460a41] decimal/package.d:6851 void decimal.sinkGeneral!(char, decimal.integrals.unsigned!(128).unsigned).sinkGeneral(ref const(std.format.FormatSpec!(char).FormatSpec), void delegate(const(char)[]), const(decimal.integrals.unsigned!(128).unsigned), const(int), const(bool), const(decimal.RoundingMode)) [0x461ae9] decimal/package.d:6890 void decimal.sinkDecimal!(char, decimal.Decimal!(128).Decimal).sinkDecimal(ref const(std.format.FormatSpec!(char).FormatSpec), void delegate(const(char)[]), ref const(decimal.Decimal!(128).Decimal), const(decimal.RoundingMode)) [0x460208] decimal/package.d:1316 const void decimal.Decimal!(128).Decimal.toString!(char).toString(scope void delegate(const(char)[]), std.format.FormatSpec!(char).FormatSpec) [0x45fff1] /.../dmd2/linux/bin64/../../src/phobos/std/format.d:3341 void std.format.formatObject!(std.stdio.File.LockingTextWriter, decimal.Decimal!(128).Decimal, char).formatObject(ref std.stdio.File.LockingTextWriter, ref decimal.Decimal!(128).Decimal, ref const(std.format.FormatSpec!(char).FormatSpec)) [0x4620c2] /.../dmd2/linux/bin64/../../src/phobos/std/format.d:3669 void std.format.formatValue!(std.stdio.File.LockingTextWriter, decimal.Decimal!(128).Decimal, char).formatValue(ref std.stdio.File.LockingTextWriter, ref decimal.Decimal!(128).Decimal, ref const(std.format.FormatSpec!(char).FormatSpec)) [0x462069] /.../dmd2/linux/bin64/../../src/phobos/std/format.d:568 uint std.format.formattedWrite!(std.stdio.File.LockingTextWriter, char, real, real, decimal.Decimal!(128).Decimal, decimal.Decimal!(128).Decimal).formattedWrite(ref std.stdio.File.LockingTextWriter, const(char[]), real, real, decimal.Decimal!(128).Decimal, decimal.Decimal!(128).Decimal) [0x45711b] /.../dmd2/linux/bin64/../../src/phobos/std/stdio.d:1496 void std.stdio.File.writefln!(char, real, real, decimal.Decimal!(128).Decimal, decimal.Decimal!(128).Decimal).writefln(const(char[]), real, real, decimal.Decimal!(128).Decimal, decimal.Decimal!(128).Decimal) [0x456be6] /.../dmd2/linux/bin64/../../src/phobos/std/stdio.d:3797 void std.stdio.writefln!(char, real, real, decimal.Decimal!(128).Decimal, decimal.Decimal!(128).Decimal).writefln(const(char[]), real, real, decimal.Decimal!(128).Decimal, decimal.Decimal!(128).Decimal) [0x456b37] rsinus.d:12 _Dmain [0x44c27d]
 if you really need to convert decimalX values to floating point 
 counterparts, you'll need to cast them:

 x = cast(real)somedecimalvalue;

 (but update your files before this, I discovered a bug in 
 opCast)
Yea, this seems to work now.
Jan 13 2018
parent reply rumbu <rumbu rumbu.ro> writes:
On Saturday, 13 January 2018 at 18:37:10 UTC, kdevel wrote:

 I get large numerical dicrepancies and an exception:
That's because you are mixing floating point and decimal. Just to take one example: double 1.1 cannot be represented exactly as floating point and it's in fact 1.10000002384185791015625. sin is calculated using a Taylor series: sin(x) = x - x^3/3! + x^5/5! - x^7/7! ... and so on. Raising to power all that junk after 1.1 will lead finally to error. If you really want to find out sin(1.1) using decimal, try sin(decimal128("1.1")) or sin(decimal128(1)/10); For exact values like sin(1.0), I let you decide which one is more exact: Wolfram Alpha: 0.8414709848078965066525023216302989 real: 0.8414709848078965066645910000000000 double: 0.8414709848078965048700000000000000 float: 0.8414709568023681640600000000000000 decimal128: 0.8414709848078965066329679978908351 decimal64: 0.8414709848078965000000000000000000 decimal32: 0.8414710000000000000000000000000000 Anyway, I wouldn't call a difference at the 18th digit a "large discrepancy" when we are talking about irrational numbers. Regarding the exception, I cannot reproduce it, but I'll look into it. Thank you for your exhaustive testing :)
Jan 13 2018
parent reply kdevel <kdevel vogtner.de> writes:
On Saturday, 13 January 2018 at 19:28:40 UTC, rumbu wrote:
 On Saturday, 13 January 2018 at 18:37:10 UTC, kdevel wrote:

 I get large numerical dicrepancies and an exception:
That's because you are mixing floating point and decimal. Just to take one example: double 1.1 cannot be represented exactly as floating point and it's in fact 1.10000002384185791015625.
Sure. double has 53 mantissa bits but float only 24. But that is not my point. I am *not* talking about the 1e-18 but about these lines: 1.10 0.891207360061435339970703 0.878166666666666700439167 -1e-02 1.20 0.932039085967226349689098 0.932735999999999982229600 1e-04 1.30 0.963558185417192964729825 0.964774416666666678037840 1e-03 and 5.70 -0.550685542597637763537807 -25.165500000000002545915000 - At 5.7 the value is obviously out of range.
 sin is calculated using a Taylor series: sin(x) = x - x^3/3! + 
 x^5/5! - x^7/7! ... and so on. Raising to power all that junk 
 after 1.1 will lead finally to error. If you really want to 
 find out sin(1.1) using decimal, try sin(decimal128("1.1")) or 
 sin(decimal128(1)/10);
Chapter 9.2 of IEEE-754-2008 says the domain of sin(x) is (-inf, inf). So if the argument x is outside the radius of convergence of the Taylor series x must be reduced modulo 2pi such that it fits.
 For exact values like sin(1.0), I let you decide which one is 
 more exact:

 Wolfram Alpha: 0.8414709848078965066525023216302989
 real:          0.8414709848078965066645910000000000
 double:        0.8414709848078965048700000000000000
 float:         0.8414709568023681640600000000000000
 decimal128:    0.8414709848078965066329679978908351
 decimal64:     0.8414709848078965000000000000000000
 decimal32:     0.8414710000000000000000000000000000

 Anyway, I wouldn't call a difference at the 18th digit a "large 
 discrepancy" when we are talking about irrational numbers.
See above.
 Regarding the exception, I cannot reproduce it, but I'll look 
 into it.
Nice.
 Thank you for your exhaustive testing :)
There's more to come (sorry for the greek symbols, could not resist to check this utf-8 feature): unity.d ``` import std.stdio; import std.typecons; import std.range; import std.math; import decimal; immutable size_t N = 100; void unity (T) () { writeln ("\n=== ", T.stringof, " ===\n"); immutable one = T (1); immutable two = T (2); immutable π = atan (one) * 4; writefln!"π = <%30.24f>" (π); foreach (i; iota(N + 1)) { auto φ = two * π * i / N; auto sinφ = sin (φ); auto cosφ = cos (φ); auto unity = sinφ * sinφ + cosφ * cosφ; auto δ = one - unity; writeln ("φ = <", φ, ">, δ = <", δ, ">"); } } void main () { // unity!float; // unity!double; // unity!real; // unity!decimal32; unity!decimal64; unity!decimal128; } ``` Selected lines from the output produced here (Linux, DMD64 D Compiler v2.077.1): === Decimal!64 === π = < 3.141592653589793000000000> φ = <0>, δ = <0> φ = <0.0628319>, δ = <0> φ = <0.125664>, δ = <1e-16> possibly okay : φ = <0.942478>, δ = <2.41721e-06> too large : φ = <2.45044>, δ = <-6.57811e-07> ditto φ = <2.51327>, δ = <-3.83012e-10> ditto φ = <2.57611>, δ = <0.479476> ditto : : : === Decimal!128 === π = < 3.141592653589793238612055> φ = <0>, δ = <0> φ = <1e-02>, δ = <-1.73780e-22> : Something went wrong with printing φ. And also this program crashes: core.exception.RangeError decimal/package.d(6652): Range violation ---------------- ??:? _d_arrayboundsp [0x80bc65f] ??:? void decimal.sinkFloat!(char, decimal.integrals.unsigned!(128).unsigned).sinkFloat(ref const(std.format.FormatSpec!(char).FormatSpec), void delegate(const(char)[]), const(decimal.integrals.unsigned!(128).unsigned), const(int), const(bool), const(decimal.RoundingMode), const(bool)) [0x80b2976] ??:? void decimal.sinkGeneral!(char, decimal.integrals.unsigned!(128).unsigned).sinkGeneral(ref const(std.format.FormatSpec!(char).FormatSpec), void delegate(const(char)[]), const(decimal.integrals.unsigned!(128).unsigned), const(int), const(bool), const(decimal.RoundingMode)) [0x80b33a4] ??:? void decimal.sinkDecimal!(char, decimal.Decimal!(128).Decimal).sinkDecimal(ref const(std.format.FormatSpec!(char).FormatSpec), void delegate(const(char)[]), ref const(decimal.Decimal!(128).Decimal), const(decimal.RoundingMode)) [0x80b241a] ??:? const void decimal.Decimal!(128).Decimal.toString!(char).toString(scope void delegate(const(char)[]), std.format.FormatSpec!(char).FormatSpec) [0x80b22bb] ??:? void std.format.formatObject!(std.stdio.File.LockingTextWriter, decimal.Decimal!(128).Decimal, char).formatObject(ref std.stdio.File.LockingTextWriter, ref decimal.Decimal!(128).Decimal, ref const(std.format.FormatSpec!(char).FormatSpec)) [0x80b73cf] ??:? void std.format.formatValue!(std.stdio.File.LockingTextWriter, decimal.Decimal!(128).Decimal, char).formatValue(ref std.stdio.File.LockingTextWriter, ref decimal.Decimal!(128).Decimal, ref const(std.format.FormatSpec!(char).FormatSpec)) [0x80b7393] ??:? uint std.format.formattedWrite!(std.stdio.File.LockingTextWriter, char, decimal.Decimal!(128).Decimal).formattedWrite(ref std.stdio.File.LockingTextWriter, const(char[]), decimal.Decimal!(128).Decimal) [0x80b6efd] ??:? void std.stdio.File.write!(immutable(char)[], decimal.Decimal!(128).Decimal, immutable(char)[], decimal.Decimal!(128).Decimal, immutable(char)[], char).write(immutable(char)[], decimal.Decimal!(128).Decimal, immutable(char)[], decimal.Decimal!(128).Decimal, immutable(char)[], char) [0x8093176] ??:? void std.stdio.writeln!(immutable(char)[], decimal.Decimal!(128).Decimal, immutable(char)[], decimal.Decimal!(128).Decimal, immutable(char)[]).writeln(immutable(char)[], decimal.Decimal!(128).Decimal, immutable(char)[], decimal.Decimal!(128).Decimal, immutable(char)[]) [0x80930c8] ??:? void unity.unity!(decimal.Decimal!(128).Decimal).unity() [0x8092f62] ??:? _Dmain [0x80905ec]
Jan 13 2018
parent reply rumbu <rumbu rumbu.ro> writes:
On Saturday, 13 January 2018 at 20:40:20 UTC, kdevel wrote:
 On Saturday, 13 January 2018 at 19:28:40 UTC, rumbu wrote:
 On Saturday, 13 January 2018 at 18:37:10 UTC, kdevel wrote:

 I get large numerical dicrepancies and an exception:
That's because you are mixing floating point and decimal. Just to take one example: double 1.1 cannot be represented exactly as floating point and it's in fact 1.10000002384185791015625.
Sure. double has 53 mantissa bits but float only 24. But that is not my point. I am *not* talking about the 1e-18 but about these lines: 1.10 0.891207360061435339970703 0.878166666666666700439167 -1e-02 1.20 0.932039085967226349689098 0.932735999999999982229600 1e-04 1.30 0.963558185417192964729825 0.964774416666666678037840 1e-03 and 5.70 -0.550685542597637763537807 -25.165500000000002545915000 - At 5.7 the value is obviously out of range.
There was an error in 128/256 bit division. The exception might be caused also by the same division since in the output values are rounded also by division. I think now it's solved. Now it prints: 1.00 +0.841470984807896506690000 +0.8414709848078965066525023216302990 +2.653e-18 1.10 +0.891207360061435339970000 +0.8912073600614353383642161528821827 +6.642e-19 1.20 +0.932039085967226349660000 +0.9320390859672263482207034175881317 +7.207e-19 1.30 +0.963558185417192964750000 +0.9635581854171929634976039012289115 +1.998e-18 1.40 +0.985449729988460180680000 +0.9854497299884601806594745788060975 +2.659e-18 1.50 +0.997494986604054430920000 +0.9974949866040544309417233711414873 +2.942e-18 1.60 +0.999573603041505164340000 +0.9995736030415051644881114370526778 +2.988e-18 1.70 +0.991664810452468615300000 +0.9916648104524686161192003644210237 +3.119e-18 1.80 +0.973847630878195186490000 +0.9738476308781951865323731788433576 +2.532e-18 1.90 +0.946300087687414488450000 +0.9463000876874144907527365796794817 +4.753e-18 2.00 +0.909297426825681695360000 +0.9092974268256816953960198659117448 +2.896e-18 2.10 +0.863209366648873770580000 +0.8632093666488737742146820454680267 +5.515e-18 2.20 +0.808496403819590184280000 +0.8084964038195901884235447312035390 +6.724e-18 2.30 +0.745705212176720177430000 +0.7457052121767201827156143818820931 +7.216e-18 2.40 +0.675463180551150926640000 +0.6754631805511509324649212496712473 +7.965e-18 2.50 +0.598472144103956494230000 +0.5984721441039565012621472421085657 +8.762e-18 2.60 +0.515501371821464235500000 +0.5155013718214642429697257155298933 +8.470e-18 2.70 +0.427379880233829934900000 +0.4273798802338299426927023640114310 +8.893e-18 2.80 +0.334988150155904919980000 +0.3349881501559049280238548187303455 +9.224e-18 2.90 +0.239249329213982328710000 +0.2392493292139823378938385702354806 +1.009e-17 3.00 +0.141120008059867222730000 +0.1411200080598672320006697688125648 +9.751e-18 3.10 +0.041580662433290579925000 +0.0415806624332905891860497743294677 +9.416e-18 3.20 -0.058374143427579908319000 -0.0583741434275798991542696566715643 +8.966e-18 3.30 -0.157745694143248381120000 -0.1577456941432483701618970386961038 +1.049e-17 3.40 -0.255541102026831318290000 -0.2555411020268313076483241184102069 +9.852e-18 3.50 -0.350783227689619847110000 -0.3507832276896198368828885525540795 +8.817e-18 3.60 -0.442520443294852383210000 -0.4425204432948523726088679351487828 +8.991e-18 3.70 -0.529836140908493212180000 -0.5298361409084932021854772134659022 +8.015e-18 3.80 -0.611857890942719074680000 -0.6118578909427190654510058312314653 +7.749e-18 3.90 -0.687766159183973817050000 -0.6877661591839738086537681707768678 +6.346e-18 4.00 -0.756802495307928250370000 -0.7568024953079282428752720232848741 +5.825e-18 4.10 -0.818277111064410503450000 -0.8182771110644104967923257194259585 +4.708e-18 4.20 -0.871575772413588059290000 -0.8715757724135880526646647777983275 +4.035e-18 4.30 -0.916165936749454983420000 -0.9161659367494549780197217790850059 +2.480e-18 4.40 -0.951602073889515953550000 -0.9516020738895159488107335437472529 +1.689e-18 4.50 -0.977530117665097054990000 -0.9775301176650970518056064241753746 -3.056e-19 4.60 -0.993691003633464455930000 -0.9936910036334644542315117020129026 -1.232e-18 4.70 -0.999923257564100884190000 -0.9999232575641008839565405992429380 -2.457e-18 4.80 -0.996164608835840671960000 -0.9961646088358406733565781684136726 -3.857e-18 4.90 -0.982452612624332512720000 -0.9824526126243325156335998995246860 -5.634e-18 5.00 -0.958924274663138469490000 -0.9589242746631384739990737444940666 -8.499e-18 5.10 -0.925814682327732297800000 -0.9258146823277323037497456163785133 -9.250e-18 5.20 -0.883454655720153265820000 -0.8834546557201532731063845238285883 -1.041e-17 5.30 -0.832267442223901164880000 -0.8322674422239011735432977190794839 -1.104e-17 5.40 -0.772764487555987363960000 -0.7727644875559873750523273015869194 -1.385e-17 5.50 -0.705540325570391908070000 -0.7055403255703919225313239842210499 -1.603e-17 5.60 -0.631266637872321313520000 -0.6312666378723213293016843594524164 -1.760e-17 5.70 -0.550685542597637763540000 -0.5506855425976377779216063046556619 -1.622e-17 5.80 -0.464602179413757213930000 -0.4646021794137572317851754163529263 -1.909e-17 5.90 -0.373876664830236362540000 -0.3738766648302363811468503760962045 -1.985e-17 6.00 -0.279415498198925875720000 -0.2794154981989258948954720395703132 -2.020e-17
Chapter 9.2 of IEEE-754-2008 says the domain of sin(x) is (-inf, 
inf). So if the argument >x is outside the radius of convergence 
of the Taylor series x must be reduced modulo 2pi >such that it 
fits.
That's exactly how it's done internally: https://github.com/rumbu13/decimal/blob/master/src/decimal/package.d#L13380 Your unity test also seems now to compute correctly. My maximum |δ| for decimal128 is now 1e-34
There's more to come (sorry for the greek symbols, could not 
resist to check this utf-8 feature)
I like them too: https://github.com/rumbu13/decimal/blob/master/src/decimal/package.d#L13558 Do you have something against including parts of your test published here as unittests? Thanks.
Jan 13 2018
parent reply kdevel <kdevel vogtner.de> writes:
On Saturday, 13 January 2018 at 22:05:02 UTC, rumbu wrote:
 Now it prints:

      1.00    +0.841470984807896506690000 
 +0.8414709848078965066525023216302990   +2.653e-18
My values differ slightly 1.00 0.841470984807896506664591 0.841470984807896506652502 3.653e-18 But I would check this later.
 Your unity test also seems now to compute correctly. My maximum 
 |δ| for decimal128 is now 1e-34
Same here.
 Do you have something against including parts of your test 
 published here as unittests? Thanks.
No, of course not, please take whatever you need! I have another testcase which prints awkward results: sinus_31.d ``` import std.stdio; import std.math; import decimal; void main () { real r; for (r = 1; r < 6; r += .1L) { decimal128 d = r; auto dsin = sin (d); auto rsin = sin (r); real delta = cast(real) dsin; delta -= rsin; writefln ("%9.2f %30.24f %30.24f %12.4g", r, rsin, dsin, delta); } } ``` Please note the large deltas in case of negative operands. I suspect the conversion to real is wrong for these numbers: 1.00 0.841470984807896506664591 0.841470984807896506652502 -5.421e-20 1.10 0.891207360061435339970703 0.891207360061435378734271 3.871e-17 1.20 0.932039085967226349689098 0.932039085967226332095783 -1.756e-17 1.30 0.963558185417192964729825 0.963558185417192975802550 1.106e-17 1.40 0.985449729988460180693261 0.985449729988460165022497 -1.567e-17 1.50 0.997494986604054430972058 0.997494986604054430941723 -5.421e-20 1.60 0.999573603041505164359688 0.999573603041505161845555 -2.548e-18 1.70 0.991664810452468615338453 0.991664810452468621659514 6.343e-18 1.80 0.973847630878195186514452 0.973847630878195177217087 -9.324e-18 1.90 0.946300087687414488452770 0.946300087687414518555639 3.014e-17 2.00 0.909297426825681695322298 0.909297426825681695396020 5.421e-20 2.10 0.863209366648873770600393 0.863209366648873727768840 -4.283e-17 2.20 0.808496403819590184279108 0.808496403819590083670346 -1.006e-16 2.30 0.745705212176720177432929 0.745705212176720299980194 1.226e-16 2.40 0.675463180551150926659871 0.675463180551150998092962 7.145e-17 2.50 0.598472144103956494242628 0.598472144103956494051855 -2.168e-19 2.60 0.515501371821464235509908 0.515501371821464164135960 -7.139e-17 2.70 0.427379880233829934906920 0.427379880233829779959717 -1.549e-16 2.80 0.334988150155904919986691 0.334988150155905092912764 1.729e-16 2.90 0.239249329213982328722001 0.239249329213982423338157 9.458e-17 3.00 0.141120008059867222739193 0.141120008059867222100745 -6.37e-19 3.10 0.041580662433290579926783 0.041580662433290496266481 -8.366e-17 3.20 -0.058374143427579908318645 -0.058374143427580079845624 0.1167 3.30 -0.157745694143248381115198 -0.157745694143248199327897 0.3155 3.40 -0.255541102026831318288136 -0.255541102026831224503680 0.5111 3.50 -0.350783227689619847116916 -0.350783227689619848120369 0.7016 3.60 -0.442520443294852383234834 -0.442520443294852457800917 0.885 3.70 -0.529836140908493212172466 -0.529836140908493358235883 1.06 3.80 -0.611857890942719074699619 -0.611857890942718929404559 1.224 3.90 -0.687766159183973817053612 -0.687766159183973746223590 1.376 4.00 -0.756802495307928250372134 -0.756802495307928251372639 1.514 4.10 -0.818277111064410503490831 -0.818277111064410297328416 1.637 4.20 -0.871575772413588059281658 -0.871575772413588143853178 1.743 4.30 -0.916165936749454983402223 -0.916165936749454908681465 1.832 4.40 -0.951602073889515953533484 -0.951602073889516059450567 1.903 4.50 -0.977530117665097055001684 -0.977530117665097055389135 1.955 4.60 -0.993691003633464455912887 -0.993691003633464414978127 1.987 4.70 -0.999923257564100884174467 -0.999923257564100886248443 2 4.80 -0.996164608835840671935500 -0.996164608835840688668900 1.992 4.90 -0.982452612624332512691275 -0.982452612624332448489147 1.965 5.00 -0.958924274663138469525139 -0.958924274663138468893154 1.918 5.10 -0.925814682327732297803524 -0.925814682327732436041956 1.852 5.20 -0.883454655720153265790421 -0.883454655720153186430800 1.767 5.30 -0.832267442223901164892897 -0.832267442223901270558807 1.665 5.40 -0.772764487555987363974919 -0.772764487555987145293506 1.546 5.50 -0.705540325570391908089566 -0.705540325570391906231919 1.411 5.60 -0.631266637872321313522395 -0.631266637872321596871912 1.263 5.70 -0.550685542597637763537807 -0.550685542597637621830316 1.101 5.80 -0.464602179413757213934100 -0.464602179413757384094532 0.9292 5.90 -0.373876664830236362535186 -0.373876664830236042617223 0.7478 6.00 -0.279415498198925875709877 -0.279415498198925872811555 0.5588 There's also code which one would expect to compile: lt.d ``` import std.stdio; import decimal; void main () { decimal32 d; if (d < 0) writeln ("< 0"); else writeln ("not < 0"); } ``` $ dmd -g lt.d decimal.git/libdecimal.a decimal/package.d(9404): Error: template decimal.cmp cannot deduce function from argument types !()(uint, int, bool, uint, int, bool), candidates are: decimal/package.d(2574): decimal.cmp(D1, D2)(auto ref const D1 x, auto ref const D2 y) if (isDecimal!(D1, D2)) decimal/package.d(1073): Error: template instance decimal.decimalCmp!(Decimal!32, int) error instantiating lt.d(7): instantiated from here: opCmp!int and also this one: coerce.d ``` import std.stdio; import std.typecons; import decimal; void main () { decimal32 d = "1"; auto p = 4 * d; auto q = d * 4; writeln (typeof(p).stringof); writeln (typeof(q).stringof); } ``` $ dmd -g coerce.d decimal.git/libdecimal.a decimal/package.d(8339): Error: template instance isFloatingpoint!F template 'isFloatingpoint' is not defined, did you mean isFloatingPoint? coerce.d(8): Error: template instance decimal.Decimal!32.Decimal.opBinaryRight!("*", int) error instantiating
Jan 13 2018
parent reply rumbu <rumbu rumbu.ro> writes:
On Saturday, 13 January 2018 at 22:51:18 UTC, kdevel wrote:
 On Saturday, 13 January 2018 at 22:05:02 UTC, rumbu wrote:
 Now it prints:

      1.00    +0.841470984807896506690000 
 +0.8414709848078965066525023216302990   +2.653e-18
My values differ slightly 1.00 0.841470984807896506664591 0.841470984807896506652502 3.653e-18 But I would check this later.
Can you please tell me on your system what are the values for real.sizeof and real.mant_dig?
 Please note the large deltas in case of negative operands. I 
 suspect the conversion to real is wrong for these numbers:
 -0.996164608835840688668900        1.992
      4.90    -0.982452612624332512691275    
 -0.982452612624332448489147        1.965
      5.00    -0.958924274663138469525139    
 -0.958924274663138468893154        1.918
      5.10    -0.925814682327732297803524
Forgot to propagate the sign in case of decimal to binary conversion. Corrected.
 There's also code which one would expect to compile:
It compiles now.
Jan 13 2018
parent reply kdevel <kdevel vogtner.de> writes:
On Sunday, 14 January 2018 at 07:20:26 UTC, rumbu wrote:
 On Saturday, 13 January 2018 at 22:51:18 UTC, kdevel wrote:
 [...]
Can you please tell me on your system what are the values for real.sizeof and real.mant_dig?
They are 12 and 64 (-m32) and 16 and 64 (-m64).
 [...]
      [...]
Forgot to propagate the sign in case of decimal to binary conversion. Corrected.
ACK.
[...]
It compiles now.
ACK.
Jan 14 2018
parent reply rumbu <rumbu rumbu.ro> writes:
On Sunday, 14 January 2018 at 11:44:39 UTC, kdevel wrote:
 On Sunday, 14 January 2018 at 07:20:26 UTC, rumbu wrote:
 On Saturday, 13 January 2018 at 22:51:18 UTC, kdevel wrote:
 [...]
Can you please tell me on your system what are the values for real.sizeof and real.mant_dig?
They are 12 and 64 (-m32) and 16 and 64 (-m64).
This is curious. Because the only documented real type with a 64-bit mantissa is the 80-bit Intel Extended Precision format with a unique sizeof of 10 (and not 12 or 16). Actually, real80 is the only type I'm supporting in the library, any other real type is converted to double. This will explain some loss of precision. Do you have more information about the real type on you system, because on mine (Windows), real.sizeof is always 10, irrespective of -m32 or -m64. Mantissa will be easy to extract being 64-bit, but I don't know the exact layout of your real type to extract the exponent.
Jan 14 2018
parent kdevel <kdevel vogtner.de> writes:
On Sunday, 14 January 2018 at 12:35:43 UTC, rumbu wrote:
 On Sunday, 14 January 2018 at 11:44:39 UTC, kdevel wrote:
 On Sunday, 14 January 2018 at 07:20:26 UTC, rumbu wrote:
 On Saturday, 13 January 2018 at 22:51:18 UTC, kdevel wrote:
 [...]
Can you please tell me on your system what are the values for real.sizeof and real.mant_dig?
They are 12 and 64 (-m32) and 16 and 64 (-m64).
This is curious. Because the only documented real type with a 64-bit mantissa is the 80-bit Intel Extended Precision format with a unique sizeof of 10 (and not 12 or 16).
sizeof returns the in-memory size (at least in C and C++) which is subject to alignment constraints. 32-bit mode typically requires 4-byte aligned access (96 bit = 12 byte) while 64-bit requires 8-byte alignment (128 bit = 16 bytes) for optimal performance. Cf. e.g. p. 28 of "SYSTEM V APPLICATION BINARY INTERFACE Intel386" (long double) http://www.sco.com/developers/devspecs/abi386-4.pdf and p. 12 of "System V Application Binary Interface AMD64 Architecture Processor Supplement" https://www.uclibc.org/docs/psABI-x86_64.pdf
 Actually, real80 is the only type I'm supporting in the 
 library, any other real type is converted to double. This will 
 explain some loss of precision.
Agreed.
 Do you have more information about the real type on you system, 
 because on mine (Windows), real.sizeof is always 10, 
 irrespective of -m32 or -m64.

 Mantissa will be easy to extract being 64-bit, but I don't know 
 the exact layout of your real type to extract the exponent.
Assumed that your machine has a X86_64 cpu real = long double is the 80-bit extended precision format. I suppose you are running windows and it may be that the Window ABI stores these reals 16-bit (2-byte) aligned. In this case there will be no padding. But I have not found info on this topic.
Jan 14 2018
prev sibling parent Jack Stouffer <jack jackstouffer.com> writes:
On Monday, 8 January 2018 at 22:16:25 UTC, rumbu wrote:
 Source code: 
 https://github.com/rumbu13/decimal/blob/master/src/decimal.d

 Documentation: http://rumbu13.github.io/decimal/doc/decimal.html
This looks really good. I think with a little work it would be ready for proposal into Phobos. I only wish I knew about it two months ago :/
Jan 12 2018