Closed verelpode closed 3 years ago
@Dogwei -- I see your C# port at: https://github.com/Dogwei/RyuCsharp
Thanks, that's great -- that helped me get started with Ryu faster. I suggest some improvements. I see that currently your port doesn't use System.Math.BigMul
. To achieve the desired performance goals, I suggest replacing umul128
invocations with System.Math.BigMul
as shown in my previous message.
I also suggest eliminating (or at least reducing) the usage of unsafe pointers. These unsafe pointers are unnecessary. For char
pointers, if you use .NET Framework 5.0 (or .NET Core), then you can use the safe, reliable, fast Span<char>
instead of char*
. Alternatively, instead of Span<char>
, you can use a simple char array such char[] result = new char[25];
. No need to resort to unsafe pointers.
Even when using the older .NET Framework 4.8 that doesn't support Span<T>
, you can still implement the two-dimensional array 'DOUBLE_POW5_INV_SPLIT' without using any unsafe pointers. In the mulShiftAll64
function, simplify and replace its confusing/unsafe mul
pointer parameter with 2 simple reliable uint64 parameters named mul_0
and mul_1
. This is demonstrated in my previous message.
hmmm on second point, maybe you know all this already but you wanted to make a C# Ryu version that is as close as possible to the original plain-C source code, regardless of whether it does or doesn't achieve the performance goals when System.Math.BigMul
etc are not used.
If that was your goal, I suggest you could make two versions: One that is a practical port to C# for real-world usage, and another version that is as close as possible to the plain-C source code.
See also this suggestion from @Tornhoof to implement Ryu inside a future version of the .NET Framework 5.0:
Consider implementing Ryu algorithm for double.ToString() #10939
Hi @verelpode. I intentionally picked a boring old languages, rather than an exciting new one, exactly because it is boring, close to assembly, and still pretty much universal. I'm happy to link to wherever you want to post your port or instructions for whatever modern language you feel like. However, this repo is going to stick with C. This report isn't directly actionable, so I will close it. Feel free to post the instructions (or even better, the source) somewhere and let me know. Thanks!
It seems like Ulf Adams did a great job in creating Ryu, and he made impressive optimizations. The only downside is that the "master" version is the plain-C version instead of a modern language. Nevertheless it's great work.
I wrote up instructions for porting the "d2s.c" file to C#:
Firstly, eliminate all usage of the plain-C/prehistoric
memcpy
function. Change this:To:
Where
CopyFromDigitTable
is a new method (not in the original source code) implemented like this:To port the
mulShiftAll64
function, simplify and replace its confusing/unsafemul
pointer parameter with 2 simple reliable uint64 parameters namedmul_0
andmul_1
. Change this:To use two simple uint64 parameters named
mul_0
andmul_1
instead of the unsafemul
pointer:As you see above,
mulShiftAll64
invokesmulShift64
. Likewise changemulShift64
to have 2 parametersmul_0
andmul_1
instead of the unsafemul
pointer. Also,mulShift64
invokesumul128
. Replaceumul128
invocations withSystem.Math.BigMul
:Swap "high" and "low" when replacing
umul128
withSystem.Math.BigMul
. i.e.System.Math.BigMul
andumul128
have exchanged meanings of the output parameter and return value ("low" and "high" are in swapped places).When the code invokes
mulShiftAll64
, update the invocation to pass the two newmul_0
andmul_1
parameters instead of themul
pointer: Change this:To:
Note the usage of the two-dimensional array, hence the comma in
DOUBLE_POW5_INV_SPLIT[q,0]
. This is much clearer and safer than the original confusingmul
pointer.To port the lookup table, change this:
To a C# two-dimensional array with comma in the
[,]
:To extract the float64/double to raw bits in a uint64, change this:
To use either
BitConverter.DoubleToInt64Bits
orCompilerServices.Unsafe.As
:Insert
unchecked { ... }
blocks covering the entire body of each function/method, to solve the problem of it throwing overflow exceptions whenever the C# program is compiled with overflow-checking enabled. In many places, this Ryu code is designed to deliberately cause and ignore overflows.Change every
char*
toSystem.Span<char>
. For example, change:To:
This Ryu code continues to work regardless of whether
char
is 8-bit or 16-bit.Change this:
To:
The
struct floating_decimal_64
should be changed toreadonly
:Only the
d2s_buffered_n
function/method modifies the fields insidestruct floating_decimal_64
but it is very easy to changed2s_buffered_n
to use local variables instead of the fields, followed by a final step of creating the instance offloating_decimal_64
at the end:Use
AggressiveInlining
where applicable: