Skip to content

Commit e7a1428

Browse files
Improve bit size validation in ByteSize.cs (#1595)
1 parent 6e56b08 commit e7a1428

File tree

3 files changed

+92
-10
lines changed

3 files changed

+92
-10
lines changed

src/Humanizer/Bytes/ByteSize.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -467,17 +467,13 @@ public static bool TryParse(CharSpan s, IFormatProvider? formatProvider, out Byt
467467
case ByteSymbol:
468468
if (sizePart.SequenceEqual(BitSymbol))
469469
{
470-
// Bits
471-
if (number % 1 != 0) // Can't have partial bits
472-
{
473-
return false;
474-
}
475-
476-
result = FromBits((long) number);
470+
if (!double.IsFinite(number)) return false;
471+
if (number != Math.Truncate(number)) return false;
472+
if (number < long.MinValue || number > long.MaxValue) return false;
473+
result = FromBits((long)number);
477474
}
478475
else
479476
{
480-
// Bytes
481477
result = FromBytes(number);
482478
}
483479

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#if !(NET5_0_OR_GREATER)
2+
3+
using System.Runtime.CompilerServices;
4+
5+
namespace System;
6+
7+
8+
// C# 14 static extension members on the *type* itself.
9+
// Usage on old TFMs: Double.IsFinite(x), Double.IsIntegral(x), Single.IsFinite(x), ...
10+
static class FloatCompat
11+
{
12+
// ---- Double ----
13+
extension(double)
14+
{
15+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
16+
public static bool IsFinite(double x)
17+
{
18+
var bits = (ulong)BitConverter.DoubleToInt64Bits(x);
19+
return (bits & 0x7FF0_0000_0000_0000UL) != 0x7FF0_0000_0000_0000UL;
20+
}
21+
22+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
23+
public static bool IsIntegral(double x)
24+
{
25+
if (!IsFinite(x)) return false;
26+
return x == Math.Truncate(x);
27+
}
28+
29+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
30+
public static bool IsInfinityFast(double x)
31+
{
32+
var bits = (ulong)BitConverter.DoubleToInt64Bits(x);
33+
return (bits & 0x7FFF_FFFF_FFFF_FFFFUL) == 0x7FF0_0000_0000_0000UL;
34+
}
35+
36+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
37+
public static bool IsNaNFast(double x)
38+
{
39+
var bits = (ulong)BitConverter.DoubleToInt64Bits(x);
40+
return (bits & 0x7FF0_0000_0000_0000UL) == 0x7FF0_0000_0000_0000UL
41+
&& (bits & 0x000F_FFFF_FFFF_FFFFUL) != 0;
42+
}
43+
44+
45+
}
46+
47+
// ---- float ----
48+
extension(float)
49+
{
50+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
51+
public static bool IsFinite(float x)
52+
{
53+
// netstandard2.0 / net48: fastest is unsafe bit-cast
54+
unsafe { var bits = *(uint*)&x; return (bits & 0x7F80_0000u) != 0x7F80_0000u; }
55+
}
56+
57+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
58+
public static bool IsIntegral(float x)
59+
{
60+
if (!IsFinite(x)) return false;
61+
return x == Math.Truncate(x);
62+
}
63+
64+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
65+
public static bool IsInfinityFast(float x)
66+
{
67+
unsafe { var bits = *(uint*)&x; return (bits & 0x7FFF_FFFFu) == 0x7F80_0000u; }
68+
}
69+
70+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
71+
public static bool IsNaNFast(float x)
72+
{
73+
unsafe
74+
{
75+
var bits = *(uint*)&x;
76+
var isExpAllOnes = (bits & 0x7F80_0000u) == 0x7F80_0000u;
77+
var hasMantissa = (bits & 0x007F_FFFFu) != 0;
78+
return isExpAllOnes && hasMantissa;
79+
}
80+
}
81+
}
82+
}
83+
#endif

src/Humanizer/ToQuantityExtensions.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public static string ToQuantity(this string input, long quantity, string? format
6262

6363
static string ToQuantity(this string input, long quantity, ShowQuantityAs showQuantityAs = ShowQuantityAs.Numeric, string? format = null, IFormatProvider? formatProvider = null)
6464
{
65-
var transformedInput = quantity == 1 || quantity == -1
65+
var transformedInput = quantity is 1 or -1
6666
? input.Singularize(inputIsKnownToBePlural: false)
6767
: input.Pluralize(inputIsKnownToBeSingular: false);
6868

@@ -93,7 +93,10 @@ static string ToQuantity(this string input, long quantity, ShowQuantityAs showQu
9393
/// </example>
9494
public static string ToQuantity(this string input, double quantity, string? format = null, IFormatProvider? formatProvider = null)
9595
{
96-
var transformedInput = quantity == 1 || quantity == -1
96+
var isFinite = !(double.IsNaN(quantity) || double.IsInfinity(quantity));
97+
var isSingular = isFinite && quantity == Math.Truncate(quantity) && Math.Abs(quantity) == 1d;
98+
99+
var transformedInput = isSingular
97100
? input.Singularize(inputIsKnownToBePlural: false)
98101
: input.Pluralize(inputIsKnownToBeSingular: false);
99102

0 commit comments

Comments
 (0)