From dennisk at netspace.net.au Wed Aug 12 11:54:20 2020
From: dennisk at netspace.net.au (Dennis Katsonis)
Date: Wed, 12 Aug 2020 21:54:20 +1000
Subject: [phobos] std.numeric returns incorrect results for integer types.
Message-ID: <5070362e-9810-a6bf-9a4d-7a60b00efaf3@netspace.net.au>
std.numeric is listed in the documentation as "Floating point numerics
functions.".
I have noted that the noramlize function for std.numeric will accept an
integer type, and report a dud result.
ulong[] arr = [540,640,44];
normalize!(typeof(arr))(arr,256); \\ arr = [0, 0, 0]
One would expect either an error, or a valid result here. I think this
is a design flaw, as the function happy returns 0 without complaint.
This would lead to incorrect behaviour for someone who missed the scant
mention that its for floats.
I think that there should be either a template constraint to reject
integral types, or that it should handle integral types correctly.
I have found the below version handles integer types correctly, *IF*
that is the path you want to take. I can make a pull request if necessary.
bool normalize(R)(R range, ElementType!(R) sum = 1)
if (isForwardRange!(R))
{
ElementType!(R) s = 0;
// Step 1: Compute sum and length of the range
static if (hasLength!(R))
{
const length = range.length;
foreach (e; range)
{
s += e;
}
}
else
{
uint length = 0;
foreach (e; range)
{
s += e;
++length;
}
}
// Step 2: perform normalization
if (s == 0)
{
if (length)
{
immutable f = sum / range.length;
foreach (ref e; range) e = f;
}
return false;
}
// The path most traveled
assert(s >= 0);
static if (isIntegral!(ElementType!(R))) {
immutable f = sum / s.to!real;
foreach (ref e; range)
e = round(e*f).to!(ElementType!(R));
} else {
immutable f = sum / s;
foreach (ref e; range)
e *= f;
}
return true;
}