---
title: Slice, truncate, and convert scalar values
summary: Slice, truncate, and convert scalar string/decimal values.
status: draft
owner: saif-docgen
last_reviewed: 2026-09-15
library: SAIF.Framework.Common
reference_type: package
operation_id: OP-033
trigger: Caller formats a value for a fixed-width field or performs a numeric test*
---

# Slice, truncate, and convert scalar values

Slice, truncate, and convert scalar string/decimal values, triggered when caller formats a value for a fixed-width field or performs a numeric test*.

| Field | Value |
| --- | --- |
| **Operation ID** | OP-033 |
| **Trigger** | Caller formats a value for a fixed-width field or performs a numeric test* |
| **Inputs** | `string` / `decimal` |
| **Outputs** | `string` / `bool` / `decimal` / `int` |
| **Primary components** | ``StringExtensions.Left`, `Right`, `Truncate`, `Repeat`, `Wrap`, `ConvertToDecimalOrZero`, `ContainsOnlyNumericCharacters`; `NumericExtensions.IsNumeric`, `Precision`, `TruncatePrecision`` |
| **Entry point** | `Common/SAIF.Common/Extensions/StringExtensions.cs` |

## Steps

1. `Left` / `Right` slice without a length guard — an over-long request raises `ArgumentOutOfRangeException`. ([BR-076](<../business-rules.md#br-076-truncate-is-length-safe-(clamps-and-tolerates-null)>))
2. `Truncate` is the safe variant: it returns null unchanged and clamps the length with `Math.Min(value.Length, length)`. ([BR-076](<../business-rules.md#br-076-truncate-is-length-safe-(clamps-and-tolerates-null)>))
3. `ContainsOnlyNumericCharacters` returns `false` for null/empty and otherwise requires `Regex.Match(target, @"\D")` to have zero length — no non-digit character anywhere. ([BR-077](../business-rules.md#br-077-a-string-contains-only-numeric-characters-only-when-it-is-non-empty-and-contains-no-non-digit-character-at-all))
4. `ConvertToDecimalOrZero` returns `0` for a null/whitespace input, otherwise `Convert.ToDecimal` (which throws `FormatException` on non-numeric text). ([BR-078](../business-rules.md#br-078-a-null-or-whitespace-decimal-string-converts-to-zero-rather-than-raising-an-error))
5. `NumericExtensions.IsNumeric` uses `int.TryParse`, so it is `int`-bounded — a decimal value, or one exceeding `Int32.MaxValue`, reports `false`. ([BR-079](../business-rules.md#br-079-the-isnumeric-test-is-32-bit-integer-bounded))
6. `Precision` reads the scale byte via `BitConverter.GetBytes(decimal.GetBits(decimalValue)[3])[2]` to return the declared decimal-place count. ([BR-080](../business-rules.md#br-080-a-decimal's-precision-is-its-declared-scale%2C-read-from-the-scale-byte-extracted-via-bitconverter-and-decimal.getbits%2C-with-trailing-zeros-counted))
7. `TruncatePrecision` short-circuits to `decimal.Truncate` when precision is 0; otherwise it scales by `10^precision`, truncates (does not round), and scales back. ([BR-081](../business-rules.md#br-081-reducing-a-decimal's-precision-truncates))
8. `Repeat` concatenates `Enumerable.Repeat(value, count)`; `Wrap` returns `$"{wrapValue}{target}{wrapValue}"`.

## Process flow

The following diagram visualizes this operation's helper methods in the order documented above.

```mermaid
flowchart TD
	Start(["Caller formats a value<br>or performs a numeric test"]) --> S1["Left / Right slice<br>(no length guard)"]
	S1 --> D1{"Requested length exceeds<br>value length?"}
	D1 -->|Yes| X1(["ArgumentOutOfRangeException"])
	D1 -->|No| S2["Truncate: return null unchanged;<br>clamp length via Math.Min"]
	S2 --> S3["ContainsOnlyNumericCharacters"]
	S3 --> D2{"Input is null or empty?"}
	D2 -->|Yes| R1["Return false"]
	D2 -->|No| D3{"Any non-digit character<br>found via regex?"}
	D3 -->|Yes| R2["Return false"]
	D3 -->|No| R3["Return true"]
	R1 --> S4
	R2 --> S4
	R3 --> S4
	S4["ConvertToDecimalOrZero"] --> D4{"Input is null<br>or whitespace?"}
	D4 -->|Yes| R4["Return 0"]
	D4 -->|No| R5["Convert.ToDecimal<br>(throws FormatException<br>on non-numeric text)"]
	R4 --> S5
	R5 --> S5
	S5["IsNumeric via int.TryParse"] --> D5{"Value is decimal or<br>exceeds Int32.MaxValue?"}
	D5 -->|Yes| R6["Return false"]
	D5 -->|No| R7["Return TryParse result"]
	R6 --> S6
	R7 --> S6
	S6["Precision: read scale byte via<br>BitConverter.GetBytes and<br>decimal.GetBits(value)[3])[2]"] --> S7["TruncatePrecision"]
	S7 --> D6{"Precision is 0?"}
	D6 -->|Yes| R8["decimal.Truncate"]
	D6 -->|No| R9["Scale by 10^precision,<br>truncate, then scale back"]
	R8 --> S8
	R9 --> S8
	S8["Repeat: concatenate via<br>Enumerable.Repeat"] --> S9["Wrap: surround value<br>with delimiter"]
	S9 --> End(["Sliced, truncated, or<br>converted scalar value"])
```

## Related documentation
- [Operations and processes](../operations-processes.md)
- [Business rules](../business-rules.md)

> **Note:** Statements marked with * were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts.

## Sources
- `Common/SAIF.Common/Extensions/StringExtensions.cs`
- `Common/SAIF.Common/Extensions/NumericExtensions.cs`
