CkNumber
Job: turn whatever a database, a payload or a form hands you into the exact C# number
type you ask for — an int, a decimal, a double — without ever throwing.
Values arrive in all sorts of wrappers: a decimal from SQL Server, a double from JSON,
the text "42" from a form field. CkNumber takes any of them — the input parameter is
object?, so you can pass a boxed number of any numeric type (boxed = a number carried
inside an object, the way a data reader hands values back) or plain text — and gives you
back the type you named. When the value is missing, not a number, or too big for the type,
you get the fallback instead — 0 unless you pick another. That is this class's standard
rejection value: the fallback, never an exception. Native-sized integers (nint, nuint)
are read like any other number; their range is the process's — 64-bit today — which only
decides what values you can hold, never how a given value converts.
Text is read the plain, invariant way: an optional sign, digits, a dot for the decimal
point, an optional exponent ("4.2e1" is 42). Grouping commas and currency symbols are
not numbers here — "1,234" and "$5" are rejected. Money-dressed text is
CkMoney's job.
Quick look
using SafeCoder.CommonKit;
CkNumber.Int32("42"); // -> 42
CkNumber.Int32("1e3"); // -> 1000 (exponent form is fine)
CkNumber.Decimal("12.50"); // -> 12.50m (trailing zero kept — ready for money)
CkNumber.Int32("$5"); // -> 0 (money text — rejected; that's CkMoney's job)
CkNumber.Int32OrNull(null); // -> null (missing stays missing)
CkNumber.IsBetween("5", 1, 10); // -> true
CkNumber.IsPositive(0, allowZero: true); // -> true
CkNumber.Fits<byte>("300"); // -> false (outside 0 to 255)
CkNumber.Clamp("250", 1, 100); // -> 100
CkNumber.Divide(5, 0, fallback: -1); // -> -1m (no crash)
CkNumber.Explain("1,234"); // -> "not a number"
Methods
The first thirteen methods are one conversion per C# number type. They all work the same way; you just pick the size and shape you need — the shared rules live here, each method's own type and range in its section below.
A value outside the type's range is rejected — never silently wrapped or clipped. So is
anything that simply is not a number: a boolean, a char, a date, NaN, an infinity,
nothing at all.
Rounding. The ten integer methods (Byte through UInt128) round a fractional value
to the nearest whole number. Halves go to the even neighbour — this is called banker's
rounding, and it stops thousands of .5 values from all drifting upward. If you would
rather refuse fractions than round them, pass exact: true.
CkNumber.Int32(12.5); // -> 12 (half rounds to the even neighbour)
CkNumber.Int32(13.5); // -> 14 (so does this one)
CkNumber.Int32(
input: "12.5",
exact: true,
fallback: 42
); // -> 42 (exact: true refuses fractions)
Single, Double and Decimal have no exact parameter — fractions are their normal
food, so there is nothing to round or refuse.
Byte
A tiny whole number from 0 to 255.
byte Byte(
object? input = null,
bool exact = false,
byte fallback = 0,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
exact |
bool |
false |
Rounds a fractional value to the nearest whole number first, halves to the even neighbour; pass true to accept only whole values, so 12.5 answers the fallback. |
fallback |
byte |
0 |
What comes back when the value cannot be converted. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering the fallback, when the value cannot be converted. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a number the type holds | The converted value: 42 |
The converted value: 42 |
Rejected: missing, not a number, outside 0 to 255, or a fraction with exact: true |
The fallback (default 0) |
Throws CkNumberException — carries Input, TargetType and Reason |
You get a byte — it holds 0 to 255.
CkNumber.Byte("42"); // -> 42
CkNumber.Byte("300"); // -> 0 (too big for a byte — the fallback)
SByte
A tiny whole number that allows a minus sign.
sbyte SByte(
object? input = null,
bool exact = false,
sbyte fallback = 0,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
exact |
bool |
false |
Rounds a fractional value to the nearest whole number first, halves to the even neighbour; pass true to accept only whole values, so 12.5 answers the fallback. |
fallback |
sbyte |
0 |
What comes back when the value cannot be converted. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering the fallback, when the value cannot be converted. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a number the type holds | The converted value: -100 |
The converted value: -100 |
Rejected: missing, not a number, outside -128 to 127, or a fraction with exact: true |
The fallback (default 0) |
Throws CkNumberException — carries Input, TargetType and Reason |
You get an sbyte — it holds -128 to 127. Works like
Byte, with the range shifted to make room
for negatives.
CkNumber.SByte("-100"); // -> -100
Int16
A small whole number.
short Int16(
object? input = null,
bool exact = false,
short fallback = 0,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
exact |
bool |
false |
Rounds a fractional value to the nearest whole number first, halves to the even neighbour; pass true to accept only whole values, so 12.5 answers the fallback. |
fallback |
short |
0 |
What comes back when the value cannot be converted. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering the fallback, when the value cannot be converted. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a number the type holds | The converted value: 32000 |
The converted value: 32000 |
Rejected: missing, not a number, outside -32,768 to 32,767, or a fraction with exact: true |
The fallback (default 0) |
Throws CkNumberException — carries Input, TargetType and Reason |
You get a short — it holds -32,768 to 32,767.
CkNumber.Int16("32000"); // -> 32000
UInt16
A small whole number that is never negative.
ushort UInt16(
object? input = null,
bool exact = false,
ushort fallback = 0,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
exact |
bool |
false |
Rounds a fractional value to the nearest whole number first, halves to the even neighbour; pass true to accept only whole values, so 12.5 answers the fallback. |
fallback |
ushort |
0 |
What comes back when the value cannot be converted. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering the fallback, when the value cannot be converted. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a number the type holds | The converted value: 65535 |
The converted value: 65535 |
Rejected: missing, not a number, outside 0 to 65,535, or a fraction with exact: true |
The fallback (default 0) |
Throws CkNumberException — carries Input, TargetType and Reason |
You get a ushort — it holds 0 to 65,535. The U is for unsigned: the type has no
minus sign, so a negative value is rejected — the same goes for the other U types below.
CkNumber.UInt16("65535"); // -> 65535
CkNumber.UInt16("-1"); // -> 0 (negative — the fallback)
Int32
The everyday whole number.
int Int32(
object? input = null,
bool exact = false,
int fallback = 0,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
exact |
bool |
false |
Rounds a fractional value to the nearest whole number first, halves to the even neighbour; pass true to accept only whole values, so 12.5 answers the fallback. |
fallback |
int |
0 |
What comes back when the value cannot be converted. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering the fallback, when the value cannot be converted. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a number the type holds | The converted value: 42 |
The converted value: 42 |
Rejected: missing, not a number, outside about ±2.1 billion, or a fraction with exact: true |
The fallback (default 0) |
Throws CkNumberException — carries Input, TargetType and Reason |
You get an int — it holds about ±2.1 billion. This is the type most ids and counts
live in.
CkNumber.Int32("12.00"); // -> 12 (a whole number in decimal clothes)
UInt32
The everyday whole number without the minus sign.
uint UInt32(
object? input = null,
bool exact = false,
uint fallback = 0,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
exact |
bool |
false |
Rounds a fractional value to the nearest whole number first, halves to the even neighbour; pass true to accept only whole values, so 12.5 answers the fallback. |
fallback |
uint |
0 |
What comes back when the value cannot be converted. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering the fallback, when the value cannot be converted. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a number the type holds | The converted value: 4000000000 |
The converted value: 4000000000 |
Rejected: missing, not a number, outside 0 to about 4.3 billion, or a fraction with exact: true |
The fallback (default 0) |
Throws CkNumberException — carries Input, TargetType and Reason |
You get a uint — it holds 0 to about 4.3 billion.
CkNumber.UInt32("4000000000"); // -> 4000000000 (too big for an int, fine here)
Int64
A big whole number.
long Int64(
object? input = null,
bool exact = false,
long fallback = 0,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
exact |
bool |
false |
Rounds a fractional value to the nearest whole number first, halves to the even neighbour; pass true to accept only whole values, so 12.5 answers the fallback. |
fallback |
long |
0 |
What comes back when the value cannot be converted. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering the fallback, when the value cannot be converted. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a number the type holds | The converted value: 9000000000 |
The converted value: 9000000000 |
Rejected: missing, not a number, outside about ±9.2 quintillion, or a fraction with exact: true |
The fallback (default 0) |
Throws CkNumberException — carries Input, TargetType and Reason |
You get a long — it holds about ±9.2 quintillion. Pick it when an int might overflow.
CkNumber.Int64("9000000000"); // -> 9000000000 (too big for an int, fine for a long)
UInt64
A big whole number without the minus sign.
ulong UInt64(
object? input = null,
bool exact = false,
ulong fallback = 0,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
exact |
bool |
false |
Rounds a fractional value to the nearest whole number first, halves to the even neighbour; pass true to accept only whole values, so 12.5 answers the fallback. |
fallback |
ulong |
0 |
What comes back when the value cannot be converted. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering the fallback, when the value cannot be converted. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a number the type holds | The converted value: 42 |
The converted value: 42 |
Rejected: missing, not a number, outside 0 to about 18.4 quintillion, or a fraction with exact: true |
The fallback (default 0) |
Throws CkNumberException — carries Input, TargetType and Reason |
You get a ulong — it holds 0 to about 18.4 quintillion.
CkNumber.UInt64("42"); // -> 42
Int128
A huge whole number.
System.Int128 Int128(
object? input = null,
bool exact = false,
System.Int128 fallback = default,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
exact |
bool |
false |
Rounds a fractional value to the nearest whole number first, halves to the even neighbour; pass true to accept only whole values, so 12.5 answers the fallback. |
fallback |
System.Int128 |
default (0) |
What comes back when the value cannot be converted. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering the fallback, when the value cannot be converted. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a number the type holds | The converted value: 9300000000000000000000 |
The converted value: 9300000000000000000000 |
Rejected: missing, not a number, outside about ±1.7 × 10³⁸, or a fraction with exact: true |
The fallback (default 0) |
Throws CkNumberException — carries Input, TargetType and Reason |
You get a System.Int128 — it holds about ±1.7 × 10³⁸. Works like
Int64, just far bigger.
CkNumber.Int128("9300000000000000000000"); // -> 9300000000000000000000 (past long's ceiling)
UInt128
A huge whole number without the minus sign.
System.UInt128 UInt128(
object? input = null,
bool exact = false,
System.UInt128 fallback = default,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
exact |
bool |
false |
Rounds a fractional value to the nearest whole number first, halves to the even neighbour; pass true to accept only whole values, so 12.5 answers the fallback. |
fallback |
System.UInt128 |
default (0) |
What comes back when the value cannot be converted. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering the fallback, when the value cannot be converted. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a number the type holds | The converted value: 42 |
The converted value: 42 |
Rejected: missing, not a number, outside 0 to about 3.4 × 10³⁸, or a fraction with exact: true |
The fallback (default 0) |
Throws CkNumberException — carries Input, TargetType and Reason |
You get a System.UInt128 — it holds 0 to about 3.4 × 10³⁸.
CkNumber.UInt128("-1"); // -> 0 (negative — the fallback)
Single
A compact number with a decimal point.
float Single(
object? input = null,
float fallback = 0,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
fallback |
float |
0 |
What comes back when the value cannot be converted. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering the fallback, when the value cannot be converted. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a number the type holds | The converted value: 12.5f |
The converted value: 12.5f |
Rejected: missing, not a number (NaN and infinities included), or beyond float's range |
The fallback (default 0) |
Throws CkNumberException — carries Input, TargetType and Reason |
You get a float — about 7 significant digits.
CkNumber.Single("12.5"); // -> 12.5f
Double
The everyday number with a decimal point.
double Double(
object? input = null,
double fallback = 0,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
fallback |
double |
0 |
What comes back when the value cannot be converted. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering the fallback, when the value cannot be converted. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a number the type holds | The converted value: 2.5 |
The converted value: 2.5 |
Rejected: missing, not a number (NaN and infinities included), or beyond double's range |
The fallback (default 0) |
Throws CkNumberException — carries Input, TargetType and Reason |
You get a double — about 15 significant digits. This is the type JSON numbers usually
arrive as.
CkNumber.Double("2.5"); // -> 2.5
Decimal
The exact number for money.
decimal Decimal(
object? input = null,
decimal fallback = 0,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
fallback |
decimal |
0 |
What comes back when the value cannot be converted. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering the fallback, when the value cannot be converted. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a number the type holds | The converted value: 12.50m |
The converted value: 12.50m |
Rejected: missing, not a number (NaN and infinities included), or beyond decimal's range |
The fallback (default 0) |
Throws CkNumberException — carries Input, TargetType and Reason |
You get a decimal — 28 significant digits, exact — right for money.
CkNumber.Decimal("12.50"); // -> 12.50m (trailing zero kept — ready for money)
CkNumber.Decimal(
input: "1e30",
fallback: 42
); // -> 42m (beyond decimal's range)
Each conversion has a twin ending in OrNull — Int32OrNull gives an int?,
DecimalOrNull a decimal?, and so on for all thirteen. Instead of a stand-in fallback,
the twin answers null when there is nothing usable: a missing value (null or DBNull),
a value that cannot be read — and, by default, a zero, because in many database columns
0 means "not set". That makes these the right pick for filling a nullable column: the
column receives an honest NULL, never a fake number.
When zero is a real value in your data — a count, a balance — switch that off with
treatZeroAsMissing: false.
The integer twins take exact too; the Single/Double/Decimal twins do not, for the
same reason as above.
These twins pair naturally with CkDictionary when you are pulling values
out of a parsed payload, and with CkSentinel.ToNull when you are writing to storage.
ByteOrNull
The OrNull twin of Byte.
byte? ByteOrNull(
object? input = null,
bool exact = false,
bool treatZeroAsMissing = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
exact |
bool |
false |
Rounds a fractional value to the nearest whole number first, halves to the even neighbour; pass true to accept only whole values, so 12.5 answers null. |
treatZeroAsMissing |
bool |
true |
Reads a zero as missing, so it comes back as null too; pass false to keep a zero as the real value it sometimes is. |
Returns
| When | You get |
|---|---|
| The value converts to a usable number | The converted value: 42 |
Missing (null or DBNull), unreadable — not a number, outside 0 to 255, or a fraction with exact: true — or a zero with treatZeroAsMissing on (the default) |
null — this method never throws |
You get a byte? — Byte's conversion rules, null instead of a fallback.
CkNumber.ByteOrNull("300"); // -> null (too big for a byte)
SByteOrNull
The OrNull twin of SByte.
sbyte? SByteOrNull(
object? input = null,
bool exact = false,
bool treatZeroAsMissing = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
exact |
bool |
false |
Rounds a fractional value to the nearest whole number first, halves to the even neighbour; pass true to accept only whole values, so 12.5 answers null. |
treatZeroAsMissing |
bool |
true |
Reads a zero as missing, so it comes back as null too; pass false to keep a zero as the real value it sometimes is. |
Returns
| When | You get |
|---|---|
| The value converts to a usable number | The converted value: -100 |
Missing (null or DBNull), unreadable — not a number, outside -128 to 127, or a fraction with exact: true — or a zero with treatZeroAsMissing on (the default) |
null — this method never throws |
You get an sbyte?.
CkNumber.SByteOrNull(0); // -> null (zero reads as "not set" by default)
Int16OrNull
The OrNull twin of Int16.
short? Int16OrNull(
object? input = null,
bool exact = false,
bool treatZeroAsMissing = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
exact |
bool |
false |
Rounds a fractional value to the nearest whole number first, halves to the even neighbour; pass true to accept only whole values, so 12.5 answers null. |
treatZeroAsMissing |
bool |
true |
Reads a zero as missing, so it comes back as null too; pass false to keep a zero as the real value it sometimes is. |
Returns
| When | You get |
|---|---|
| The value converts to a usable number | The converted value: 32000 |
Missing (null or DBNull), unreadable — not a number, outside -32,768 to 32,767, or a fraction with exact: true — or a zero with treatZeroAsMissing on (the default) |
null — this method never throws |
You get a short?.
CkNumber.Int16OrNull("junk"); // -> null (unreadable)
UInt16OrNull
The OrNull twin of UInt16.
ushort? UInt16OrNull(
object? input = null,
bool exact = false,
bool treatZeroAsMissing = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
exact |
bool |
false |
Rounds a fractional value to the nearest whole number first, halves to the even neighbour; pass true to accept only whole values, so 12.5 answers null. |
treatZeroAsMissing |
bool |
true |
Reads a zero as missing, so it comes back as null too; pass false to keep a zero as the real value it sometimes is. |
Returns
| When | You get |
|---|---|
| The value converts to a usable number | The converted value: 65535 |
Missing (null or DBNull), unreadable — not a number, outside 0 to 65,535, or a fraction with exact: true — or a zero with treatZeroAsMissing on (the default) |
null — this method never throws |
You get a ushort?.
CkNumber.UInt16OrNull(null); // -> null (missing stays missing)
Int32OrNull
The OrNull twin of Int32.
int? Int32OrNull(
object? input = null,
bool exact = false,
bool treatZeroAsMissing = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
exact |
bool |
false |
Rounds a fractional value to the nearest whole number first, halves to the even neighbour; pass true to accept only whole values, so 12.5 answers null. |
treatZeroAsMissing |
bool |
true |
Reads a zero as missing, so it comes back as null too; pass false to keep a zero as the real value it sometimes is. |
Returns
| When | You get |
|---|---|
| The value converts to a usable number | The converted value: 42 |
Missing (null or DBNull), unreadable — not a number, outside about ±2.1 billion, or a fraction with exact: true — or a zero with treatZeroAsMissing on (the default) |
null — this method never throws |
You get an int? — the twin you will reach for most when filling nullable columns.
CkNumber.Int32OrNull("42"); // -> 42
CkNumber.Int32OrNull(0); // -> null (zero reads as "not set" by default)
CkNumber.Int32OrNull("junk"); // -> null (unreadable)
CkNumber.Int32OrNull("0.4"); // -> null (rounds to 0 first — then zero means missing)
CkNumber.Int32OrNull(
input: 0,
treatZeroAsMissing: false
); // -> 0 (a real zero, kept on request)
UInt32OrNull
The OrNull twin of UInt32.
uint? UInt32OrNull(
object? input = null,
bool exact = false,
bool treatZeroAsMissing = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
exact |
bool |
false |
Rounds a fractional value to the nearest whole number first, halves to the even neighbour; pass true to accept only whole values, so 12.5 answers null. |
treatZeroAsMissing |
bool |
true |
Reads a zero as missing, so it comes back as null too; pass false to keep a zero as the real value it sometimes is. |
Returns
| When | You get |
|---|---|
| The value converts to a usable number | The converted value: 4000000000 |
Missing (null or DBNull), unreadable — not a number, outside 0 to about 4.3 billion, or a fraction with exact: true — or a zero with treatZeroAsMissing on (the default) |
null — this method never throws |
You get a uint?.
CkNumber.UInt32OrNull("-1"); // -> null (negative — nothing usable)
Int64OrNull
The OrNull twin of Int64.
long? Int64OrNull(
object? input = null,
bool exact = false,
bool treatZeroAsMissing = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
exact |
bool |
false |
Rounds a fractional value to the nearest whole number first, halves to the even neighbour; pass true to accept only whole values, so 12.5 answers null. |
treatZeroAsMissing |
bool |
true |
Reads a zero as missing, so it comes back as null too; pass false to keep a zero as the real value it sometimes is. |
Returns
| When | You get |
|---|---|
| The value converts to a usable number | The converted value: 9000000000 |
Missing (null or DBNull), unreadable — not a number, outside about ±9.2 quintillion, or a fraction with exact: true — or a zero with treatZeroAsMissing on (the default) |
null — this method never throws |
You get a long?.
CkNumber.Int64OrNull(DBNull.Value); // -> null (a database NULL is missing too)
UInt64OrNull
The OrNull twin of UInt64.
ulong? UInt64OrNull(
object? input = null,
bool exact = false,
bool treatZeroAsMissing = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
exact |
bool |
false |
Rounds a fractional value to the nearest whole number first, halves to the even neighbour; pass true to accept only whole values, so 12.5 answers null. |
treatZeroAsMissing |
bool |
true |
Reads a zero as missing, so it comes back as null too; pass false to keep a zero as the real value it sometimes is. |
Returns
| When | You get |
|---|---|
| The value converts to a usable number | The converted value: 42 |
Missing (null or DBNull), unreadable — not a number, outside 0 to about 18.4 quintillion, or a fraction with exact: true — or a zero with treatZeroAsMissing on (the default) |
null — this method never throws |
You get a ulong?.
CkNumber.UInt64OrNull("42"); // -> 42
Int128OrNull
The OrNull twin of Int128.
System.Int128? Int128OrNull(
object? input = null,
bool exact = false,
bool treatZeroAsMissing = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
exact |
bool |
false |
Rounds a fractional value to the nearest whole number first, halves to the even neighbour; pass true to accept only whole values, so 12.5 answers null. |
treatZeroAsMissing |
bool |
true |
Reads a zero as missing, so it comes back as null too; pass false to keep a zero as the real value it sometimes is. |
Returns
| When | You get |
|---|---|
| The value converts to a usable number | The converted value: 9300000000000000000000 |
Missing (null or DBNull), unreadable — not a number, outside about ±1.7 × 10³⁸, or a fraction with exact: true — or a zero with treatZeroAsMissing on (the default) |
null — this method never throws |
You get a System.Int128?.
CkNumber.Int128OrNull("9300000000000000000000"); // -> 9300000000000000000000
UInt128OrNull
The OrNull twin of UInt128.
System.UInt128? UInt128OrNull(
object? input = null,
bool exact = false,
bool treatZeroAsMissing = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
exact |
bool |
false |
Rounds a fractional value to the nearest whole number first, halves to the even neighbour; pass true to accept only whole values, so 12.5 answers null. |
treatZeroAsMissing |
bool |
true |
Reads a zero as missing, so it comes back as null too; pass false to keep a zero as the real value it sometimes is. |
Returns
| When | You get |
|---|---|
| The value converts to a usable number | The converted value: 42 |
Missing (null or DBNull), unreadable — not a number, outside 0 to about 3.4 × 10³⁸, or a fraction with exact: true — or a zero with treatZeroAsMissing on (the default) |
null — this method never throws |
You get a System.UInt128?.
CkNumber.UInt128OrNull(0); // -> null (zero reads as "not set" by default)
SingleOrNull
The OrNull twin of Single.
float? SingleOrNull(
object? input = null,
bool treatZeroAsMissing = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
treatZeroAsMissing |
bool |
true |
Reads a zero as missing, so it comes back as null too; pass false to keep a zero as the real value it sometimes is. |
Returns
| When | You get |
|---|---|
| The value converts to a usable number | The converted value: 12.5f |
Missing (null or DBNull), unreadable — not a number (NaN and infinities included), or beyond float's range — or a zero with treatZeroAsMissing on (the default) |
null — this method never throws |
You get a float?.
CkNumber.SingleOrNull("12.5"); // -> 12.5f
DoubleOrNull
The OrNull twin of Double.
double? DoubleOrNull(
object? input = null,
bool treatZeroAsMissing = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
treatZeroAsMissing |
bool |
true |
Reads a zero as missing, so it comes back as null too; pass false to keep a zero as the real value it sometimes is. |
Returns
| When | You get |
|---|---|
| The value converts to a usable number | The converted value: 2.5 |
Missing (null or DBNull), unreadable — not a number (NaN and infinities included), or beyond double's range — or a zero with treatZeroAsMissing on (the default) |
null — this method never throws |
You get a double?.
CkNumber.DoubleOrNull(0.0); // -> null (zero reads as "not set" by default)
DecimalOrNull
The OrNull twin of Decimal.
decimal? DecimalOrNull(
object? input = null,
bool treatZeroAsMissing = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
treatZeroAsMissing |
bool |
true |
Reads a zero as missing, so it comes back as null too; pass false to keep a zero as the real value it sometimes is. |
Returns
| When | You get |
|---|---|
| The value converts to a usable number | The converted value: 19.99m |
Missing (null or DBNull), unreadable — not a number (NaN and infinities included), or beyond decimal's range — or a zero with treatZeroAsMissing on (the default) |
null — this method never throws |
You get a decimal? — the right shape for a nullable money or rate column.
CkNumber.DecimalOrNull("19.99"); // -> 19.99m
Percent
A percentage as the rate it stands for.
decimal Percent(
object? input = null,
decimal fallback = 0,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: text with or without a trailing %, or a number. |
fallback |
decimal |
0 |
What comes back when the value cannot be read. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering the fallback, when the value cannot be read. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a percentage or a bare rate | The rate: 0.15m |
The rate: 0.15m |
| Rejected: missing, or unreadable — grouping, currency symbols, junk | The fallback (default 0) |
Throws CkNumberException — carries Input, TargetType and Reason |
Percent reads a percentage and answers the rate — the number you would multiply by. A
trailing % (with or without a space before it) divides the value by 100. A bare value
— no % sign — is taken as the rate it already is, untouched.
CkNumber.Percent("15%"); // -> 0.15m
CkNumber.Percent("2.5 %"); // -> 0.025m
CkNumber.Percent("0.15"); // -> 0.15m (bare values are rates already)
CkNumber.Percent(15); // -> 15m (bare 15 is the rate 15 — that is 1,500%!)
CkNumber.Percent(
input: "junk",
fallback: -1
); // -> -1m
That last pair is the big gotcha: CkNumber never guesses which scale a bare number meant.
0.15 and 15 are both perfectly honest rates, so rescaling one by guess would corrupt
the other. If your form posts whole percent numbers without the sign, send the % along or
divide by 100 upstream.
PercentOrNull
The OrNull twin of Percent.
decimal? PercentOrNull(
object? input = null,
bool treatZeroAsMissing = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: text with or without a trailing %, or a number. |
treatZeroAsMissing |
bool |
true |
Reads a zero rate as missing, so it comes back as null too; pass false to keep a real 0%. |
Returns
| When | You get |
|---|---|
| The value reads as a usable rate | The rate: 0.15m |
Missing (null or DBNull), unreadable, or a zero rate with treatZeroAsMissing on (the default) |
null — this method never throws |
PercentOrNull is the family's OrNull twin: null for a missing value, an unreadable
one, or — by default — a zero rate (treatZeroAsMissing: false keeps a real 0%).
CkNumber.PercentOrNull("0%"); // -> null (0% reads as "not set" by default)
IsNumber
Does it read as a number at all?
bool IsNumber(
object? input = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or plain invariant text. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering false, when the value is not a number. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
A boxed number of any type, or plain invariant text such as "42", "-1.5", "4.2e1" |
true |
true |
| Rejected: not a number — missing, grouping or a currency symbol, a boolean, NaN | false |
Throws CkNumberException — carries Input, TargetType and Reason |
The yes-or-no form of the readers, under their rules: "1,234" and "$5" are not numbers
here, that is CkMoney's job; booleans, chars, dates, NaN and the infinities
are not numbers either. Zero is a number here, whatever the OrNull family says about it.
When the value is going to be stored, read it with the typed OrNull reader instead and
check for null.
CkNumber.IsNumber("4.2e1"); // -> true
CkNumber.IsNumber("1,234"); // -> false (grouping — CkMoney's job)
CkNumber.IsNumber(true); // -> false
IsInteger
A whole number?
bool IsInteger(
object? input = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or plain invariant text. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering false, when the value is not a number. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
A number with no fractional part: 12, "12.0", "1e3" |
true |
true |
A number with a fraction: "12.5", 0.1 |
false |
false |
| Rejected: not a number — missing, grouping or a currency symbol, a boolean, NaN | false |
Throws CkNumberException — carries Input, TargetType and Reason |
The exact: true rule of the integer readers, as a verdict, without choosing a width — a
value too large for Int128 is still whole. HasDecimals with its default
of 0 asks the same question.
CkNumber.IsInteger("12.0"); // -> true
CkNumber.IsInteger("12.5"); // -> false
IsPositive
Above zero?
bool IsPositive(
object? input = null,
bool allowZero = false,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or plain invariant text. |
allowZero |
bool |
false |
Count zero as well. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering false, when the value is not a number. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
A number above zero — or zero itself with allowZero |
true |
true |
| Zero (by default), or a number below it | false |
false |
| Rejected: not a number — missing, grouping or a currency symbol, a boolean, NaN | false |
Throws CkNumberException — carries Input, TargetType and Reason |
Strict by default: "quantity must be positive". With allowZero: true it is "must not
be negative": a balance, a count that may be empty.
CkNumber.IsPositive("0.5"); // -> true
CkNumber.IsPositive(0); // -> false
CkNumber.IsPositive(0, allowZero: true); // -> true
IsNegative
Below zero?
bool IsNegative(
object? input = null,
bool allowZero = false,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or plain invariant text. |
allowZero |
bool |
false |
Count zero as well. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering false, when the value is not a number. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
A number below zero — or zero itself with allowZero |
true |
true |
| Zero (by default), or a number above it | false |
false |
| Rejected: not a number — missing, grouping or a currency symbol, a boolean, NaN | false |
Throws CkNumberException — carries Input, TargetType and Reason |
The mirror of IsPositive. IsNegative, IsZero and IsPositive
partition the numbers: exactly one of them is true for any value that reads.
CkNumber.IsNegative("-0.5"); // -> true
CkNumber.IsNegative(0); // -> false
IsZero
Exactly zero?
bool IsZero(
object? input = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or plain invariant text. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering false, when the value is not a number. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
A number equal to zero: 0, "0.0", "-0" |
true |
true |
| Any other number | false |
false |
| Rejected: not a number — missing, grouping or a currency symbol, a boolean, NaN | false |
Throws CkNumberException — carries Input, TargetType and Reason |
The OrNull readers call zero "missing" by default; this says so out loud, for
data-quality checks and for telling "nobody set it" from "not a number" — the same job
CkUuid.IsNil and CkDate.IsPlaceholder
do. Note that Int32OrNull("0.4") is null because 0.4 rounds to a missing 0, while
IsZero("0.4") is false: it is not zero.
CkNumber.IsZero("0.0"); // -> true
CkNumber.IsZero("junk"); // -> false (not a number, not zero)
IsBetween
Inside a range?
bool IsBetween(
object? input = null,
object? min = null,
object? max = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or plain invariant text. |
min |
object? |
null |
The lower bound, inclusive, read the same way. null means no lower bound. |
max |
object? |
null |
The upper bound, inclusive, read the same way. null means no upper bound. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says which value is not a number and why — or that the bounds contradict each other — instead of answering false. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value and the given bounds read, and the value lies inside, both ends included | true |
true |
| Everything reads, and the value lies outside | false |
false |
| Rejected: an unreadable value or bound, or bounds that contradict each other | false |
Throws CkNumberException — carries Input, TargetType and Reason |
IsBetween(age, 0, 120). Bounds are read the same way as the value, so "100" and 100
both work, and a null bound is open: IsBetween(qty, 1, null) is "at least one".
Comparison is exact, in decimal; only a value beyond its range compares as a double.
CkNumber.IsBetween("5", 1, 10); // -> true
CkNumber.IsBetween(10, 1, 10); // -> true (both ends count)
CkNumber.IsBetween("0.15", 0.1, 0.2); // -> true (exact decimals)
CkNumber.IsBetween(5, 10, 1); // -> false (contradictory bounds)
IsAtLeast
At least this much?
bool IsAtLeast(
object? input = null,
object? min = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or plain invariant text. |
min |
object? |
null |
The lower bound, inclusive, read the same way. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering false, when either value is not a number. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
Both read, and the value is greater than or equal to min |
true |
true |
Both read, and the value is below min |
false |
false |
| Rejected: either is not a number | false |
Throws CkNumberException — carries Input, TargetType and Reason |
IsAtLeast(qty, 1), IsAtLeast(age, 18). The bound counts. The same as
IsBetween with an open upper bound, spelled the way validation code reads.
CkNumber.IsAtLeast(5, 5); // -> true
CkNumber.IsAtLeast("4.5", 5); // -> false
IsAtMost
At most this much?
bool IsAtMost(
object? input = null,
object? max = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or plain invariant text. |
max |
object? |
null |
The upper bound, inclusive, read the same way. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering false, when either value is not a number. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
Both read, and the value is less than or equal to max |
true |
true |
Both read, and the value is above max |
false |
false |
| Rejected: either is not a number | false |
Throws CkNumberException — carries Input, TargetType and Reason |
IsAtMost(items, 100), IsAtMost(rate, 1). The bound counts. The mirror of
IsAtLeast.
CkNumber.IsAtMost(100, 100); // -> true
CkNumber.IsAtMost("100.01", 100); // -> false
HasDecimals
No more than so many decimal places?
bool HasDecimals(
object? input = null,
int atMost = 0,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or plain invariant text. |
atMost |
int |
0 |
The most decimal places allowed. Below 0 acts as 0. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering false, when the value is not a number. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
A number with at most atMost decimal places once trailing zeros are dropped |
true |
true |
| A number that needs more | false |
false |
| Rejected: not a number — missing, grouping or a currency symbol, a boolean, NaN | false |
Throws CkNumberException — carries Input, TargetType and Reason |
Judged on the value, not its spelling: "12.50" has one decimal place, "12.00" has
none, 12.345 has three. For quantities and rates with a precision limit — "no more than
two decimals". With the default of 0 it is IsInteger.
CkNumber.HasDecimals("12.50", 1); // -> true
CkNumber.HasDecimals("12.50", 0); // -> false
CkNumber.HasDecimals("12.345", 2); // -> false
IsEven
An even whole number?
bool IsEven(
object? input = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or plain invariant text. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering false, when the value is not a number. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
A whole, even number: 2, "4", "10.0", 0 |
true |
true |
| An odd number, or one with a fraction, which has no parity | false |
false |
| Rejected: not a number — missing, grouping or a currency symbol, a boolean, NaN | false |
Throws CkNumberException — carries Input, TargetType and Reason |
IsOdd is the twin. A whole number is one or the other; a fraction is neither, and so is anything that is not a number — so neither method is the other's negation.
CkNumber.IsEven("10.0"); // -> true
CkNumber.IsEven("2.5"); // -> false (no parity)
IsOdd
An odd whole number?
bool IsOdd(
object? input = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or plain invariant text. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering false, when the value is not a number. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
A whole, odd number: 3, "-3", "7.0" |
true |
true |
| An even number, or one with a fraction | false |
false |
| Rejected: not a number — missing, grouping or a currency symbol, a boolean, NaN | false |
Throws CkNumberException — carries Input, TargetType and Reason |
The twin of IsEven, with the same rules.
CkNumber.IsOdd("-3"); // -> true
CkNumber.IsOdd(0); // -> false
Fits
Does it convert to this type without loss?
bool Fits<T>(
object? input = null,
bool exact = true,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
where T : struct, INumber<T>, IMinMaxValue<T>
| Parameter | Type | Default | What it does |
|---|---|---|---|
T |
type parameter | — | The numeric type to test against: int, decimal, byte … |
input |
object? |
null |
The value: a number of any type, or plain invariant text. |
exact |
bool |
true |
Refuse a fractional value for an integer type. Pass false to round it first, as the readers do. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering false, when the value does not fit. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
The value converts to T under the typed reader's rules |
true |
true |
| Rejected: out of the type's range, a fraction for an integer type, or not a number | false |
Throws CkNumberException — carries Input, TargetType and Reason — with TargetType set to T |
One generic check in place of a twin for each of the thirteen readers:
Fits<byte>("255") is true, Fits<byte>("300") and Fits<uint>(-1) are false. Unlike
the readers it refuses a fraction for an integer type by default, since "fits" means
without loss; Fits<int>("12.5", exact: false) accepts what Int32 would round.
CkNumber.Fits<byte>("300"); // -> false (outside 0 to 255)
CkNumber.Fits<int>("12.5"); // -> false (a fraction)
CkNumber.Fits<int>("12.5", exact: false); // -> true (rounds to 12)
CkNumber.Fits<double>("1e30"); // -> true
CkNumber.Fits<decimal>("1e30"); // -> false
Clamp
Read it, and keep it within bounds.
T Clamp<T>(
object? input = null,
T min = default,
T max = default,
T? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
where T : struct, INumber<T>, IMinMaxValue<T>
| Parameter | Type | Default | What it does |
|---|---|---|---|
T |
type parameter | inferred from the bounds | Clamp(x, 1, 100) answers an int, Clamp(x, 0.0, 1.0) a double. |
input |
object? |
null |
The value: a number of any type, or plain invariant text. |
min |
T |
— | The smallest value allowed. |
max |
T |
— | The largest value allowed. |
fallback |
T? |
null |
What comes back when the value cannot be read, or the bounds contradict each other; the lower bound when not given. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering the fallback, when the value cannot be read or the bounds contradict each other. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads and lies inside the bounds | The value, an integer type rounding a fraction as Int32 does |
The value |
| It reads and lies outside | The nearer bound | The nearer bound |
| Rejected: not a number, bounds that contradict each other, or a bound that is NaN | The fallback, the lower bound by default |
Throws CkNumberException — carries Input, Expected and Reason |
Page sizes, percentages, retry counts — anything a caller may set but not set wildly. The
value is read as widely as it can be before the bounds apply, so "300" still clamps to a
byte's 255.
CkNumber.Clamp("250", 1, 100); // -> 100
CkNumber.Clamp(bag["pageSize"], 1, 100); // -> 1 when the field is junk: the lower bound
CkNumber.Clamp(bag["pageSize"], 1, 100, fallback: 20); // -> 20 when the field is junk
CkNumber.Clamp("0.75", 0.0, 1.0); // -> 0.75
Round
Read it, and round it the stated way.
decimal Round(
object? input = null,
int decimals = 0,
MidpointRounding rounding = CkSettings.NumberRounding,
decimal fallback = 0,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or text. |
decimals |
int |
0 |
The places to keep, 0 to 28; outside acts as the nearer end. |
rounding |
MidpointRounding |
CkSettings.NumberRounding (ToEven) |
How a half rounds. AwayFromZero is the schoolbook rule. |
fallback |
decimal |
0 |
What comes back when the value cannot be read. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering the fallback, when the value cannot be read. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads | The rounded value, exact in decimal |
The rounded value |
| Rejected: not a number, or beyond the decimal range | The fallback |
Throws CkNumberException — carries Input, Expected and Reason |
Halves go to the even neighbour by default — Round(2.5) is 2, Round(3.5) is 4 — the
same rule the integer readers apply, which is also the framework's default and the one
that surprises everyone once. Pass MidpointRounding.AwayFromZero for 2.5 to become 3;
CkMoney.Round uses that rule for money.
CkNumber.Round("2.345", 2); // -> 2.34m
CkNumber.Round(2.5); // -> 2m (halves to even)
CkNumber.Round(2.5, rounding: MidpointRounding.AwayFromZero); // -> 3m
Divide
A quotient that cannot crash.
decimal Divide(
object? numerator = null,
object? denominator = null,
decimal fallback = 0,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
numerator |
object? |
null |
The value to divide: a number of any type, or text. |
denominator |
object? |
null |
The value to divide by, read the same way. |
fallback |
decimal |
0 |
What comes back when a value cannot be read, the denominator is zero, or the quotient is out of range. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkNumberException that says why, instead of answering the fallback, when the division cannot be done. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| Both read and the denominator is not zero | The exact quotient | The quotient |
| Rejected: a value that is not a number, a zero denominator, an out-of-range quotient | The fallback |
Throws CkNumberException — carries Input, Expected and Reason — "division by zero" names the denominator |
Divide(sold, stocked) for a ratio, Divide(total, count) for an average. Division by
zero is the commonest crash in reporting code; here it is the fallback, or a reason.
CkNumber.Divide(1, 4); // -> 0.25m
CkNumber.Divide("10", "3"); // -> 3.3333333333333333333333333333m
CkNumber.Divide(5, 0, fallback: -1); // -> -1m
Explain
Why not — in words, without an exception.
string Explain(
object? input = null
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value: a number of any type, or plain invariant text. |
Returns
| When | You get |
|---|---|
| The value passes the reader | "" |
| It does not | The reason, the same words the exception would carry — an answer, not a failure; it never throws |
The form-friendly twin of throwIfInvalid: the message to show beside the field,
without a try and catch. Every class with a reader has one, on the reader's own
rules, so the reason is exactly what the reader would say.
CkNumber.Explain("42"); // -> ""
CkNumber.Explain("1,234"); // -> "not a number"
CkNumber.Explain(null); // -> "no value"
The fallback
Every conversion method takes a fallback — the value that comes back when the input
cannot be read. It is already the right type, so it comes back exactly as you gave it,
and it defaults to 0. Pick a fallback that your code can tell apart from a real value
when that matters.
CkNumber.Int32(
input: "not a number",
fallback: -1
); // -> -1
The OrNull twins take no fallback — their answer for anything unusable is null —
and neither do the validators, whose answer is false.
Good to know
- Beyond the decimal range, integers still compare by their digits. A 128-bit value or a
run of digits past 7.9e28 keeps its exact digits for the comparisons; a double past the
range, or below 1e-28, compares as the double it is. Such a double is out of the decimal
range for
Decimaland its OrNull twin, not zero. - Grouping and currency symbols are rejected.
"1,234"and"$5"answer the fallback. Text dressed as money — symbols, thousands separators, accounting negatives — belongs to CkMoney. - Halves round to the even neighbour.
12.5becomes 12 and13.5becomes 14 — banker's rounding, so long runs of.5values do not all drift upward. Passexact: truewhen a fraction should be refused instead. Int32OrNull("0.4")isnull. The fraction rounds to 0 first, and theOrNullfamily reads zero as missing by default. PasstreatZeroAsMissing: falsewhen small values must survive as 0.- A bare percent is a rate as it stands.
Percent(15)is15m— that is 1,500%, not 15%. Only a trailing%divides by 100. Send the sign or divide upstream. NaNand infinities are not numbers here. Neither are booleans, chars or dates — they all answer the fallback (ornullfrom the twins).- Reading is culture-invariant. The decimal point is always a dot, whatever the server's regional settings — the same text reads the same everywhere.
- A
0result can mean two things — a real zero, or a rejected value falling back to the default. When you need to know which, use theOrNulltwin, pick a fallback you can recognise, or ask for an exception. - The validators treat zero as a number.
IsNumber("0"),IsInteger(0)andIsBetween(0, 0, 10)are alltrue, whatever theOrNullfamily says about zero;IsZerois the one that asks about it. All of them answerfalsefor anything unreadable, and every one takesthrowIfInvalid. - Comparisons are exact.
IsBetween,IsAtLeastandIsAtMostcompare asdecimal; only a value beyond its range, past about 7.9 × 10²⁸, compares as adouble.HasDecimalscounts on the value, so"12.50"has one decimal place.
If you want errors instead
Pass throwIfInvalid: true and a value that cannot be converted raises a
CkNumberException (from SafeCoder.CommonKit.Exceptions) instead of answering the fallback.
The exception carries the Input, the TargetType and a short Reason, flattened into a
one-line message.
CkNumber.Int32(
input: "twelve",
throwIfInvalid: true
); // throws CkNumberException — the message says why
The OrNull twins have no such switch — they never throw: null is their answer for
anything unusable. When you want an error instead, use the plain sibling with
throwIfInvalid: true.