-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrounding.ts
More file actions
181 lines (149 loc) · 5.77 KB
/
rounding.ts
File metadata and controls
181 lines (149 loc) · 5.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import { add } from "./add.js";
import compare from "./compare.js";
import { parseStringNumber, addOne, subtractOne } from "./utils";
/**
* Rounds down to the nearest integer (towards negative infinity)
* @param num Number as string
* @returns Floor value as string
*/
export function floor(num: string): string {
const parsed = parseStringNumber(num);
// If no fractional part, return the number as is
if (!parsed.fractionalPart || parsed.fractionalPart === "") {
return parsed.sign === -1 && parsed.integerPart !== "0" ? "-" + parsed.integerPart : parsed.integerPart;
}
// If positive, just return integer part
if (parsed.sign === 1) {
return parsed.integerPart;
}
// If negative, subtract 1 from integer part
const decremented = addOne(parsed.integerPart);
return "-" + decremented;
}
/**
* Rounds up to the nearest integer (towards positive infinity)
* @param num Number as string
* @returns Ceiling value as string
*/
export function ceil(num: string): string {
const parsed = parseStringNumber(num);
// If no fractional part, return the number as is
if (!parsed.fractionalPart || parsed.fractionalPart === "") {
return parsed.sign === -1 && parsed.integerPart !== "0" ? "-" + parsed.integerPart : parsed.integerPart;
}
// If negative, just return integer part (with sign)
if (parsed.sign === -1) {
return "-" + parsed.integerPart;
}
// If positive, add 1 to integer part
const incremented = addOne(parsed.integerPart);
return incremented;
}
/**
* Removes the fractional part (rounds towards zero)
* @param num Number as string
* @returns Truncated value as string
*/
export function truncate(num: string): string {
const parsed = parseStringNumber(num);
// Return just the integer part with appropriate sign
if (parsed.sign === -1 && parsed.integerPart !== "0") {
return "-" + parsed.integerPart;
}
return parsed.integerPart;
}
/**
* Rounds to the nearest integer using "round half up" strategy
* @param num Number as string
* @returns Rounded value as string
*/
export function round(num: string): string {
const parsed = parseStringNumber(num);
// If no fractional part, return the number as is
if (!parsed.fractionalPart || parsed.fractionalPart === "") {
return parsed.sign === -1 && parsed.integerPart !== "0" ? "-" + parsed.integerPart : parsed.integerPart;
}
// Check first digit of fractional part for rounding decision
const firstFracDigit = parsed.fractionalPart[0];
let firstFracValue = 0;
if (firstFracDigit >= "0" && firstFracDigit <= "9") {
firstFracValue = firstFracDigit.charCodeAt(0) - "0".charCodeAt(0);
}
// If first fractional digit is < 5, truncate
if (firstFracValue < 5) {
return truncate(num);
}
// If first fractional digit is >= 5, round up
if (parsed.sign === 1) {
// Positive number: add 1 to integer part
const incremented = addOne(parsed.integerPart);
return incremented;
} else {
// Negative number: subtract 1 from absolute value (making it more negative)
const incremented = addOne(parsed.integerPart);
return "-" + incremented;
}
}
/**
* Rounds to specified number of decimal places
* @param num Number as string
* @param precision Number of decimal places
* @returns Rounded value as string
*/
export function roundToPrecision(num: string, precision: number): string {
if (precision < 0) {
throw new Error("Precision must be non-negative");
}
const parsed = parseStringNumber(num);
// If no fractional part and precision > 0, add zeros
if (!parsed.fractionalPart || parsed.fractionalPart === "") {
if (precision === 0) {
return parsed.sign === -1 && parsed.integerPart !== "0" ? "-" + parsed.integerPart : parsed.integerPart;
}
const zeros = "0".repeat(precision);
const result = parsed.integerPart + "." + zeros;
return parsed.sign === -1 && parsed.integerPart !== "0" ? "-" + result : result;
}
// If precision is 0, use regular round function
if (precision === 0) {
return round(num);
}
// If fractional part is shorter than precision, pad with zeros
if (parsed.fractionalPart.length <= precision) {
const paddedFrac = parsed.fractionalPart.padEnd(precision, "0");
const result = parsed.integerPart + "." + paddedFrac;
return parsed.sign === -1 && parsed.integerPart !== "0" ? "-" + result : result;
}
// Get the digit at precision position for rounding decision
const roundingDigit = parsed.fractionalPart[precision];
let roundingValue = 0;
if (roundingDigit >= "0" && roundingDigit <= "9") {
roundingValue = roundingDigit.charCodeAt(0) - "0".charCodeAt(0);
}
// Take the required precision digits
let resultFrac = parsed.fractionalPart.substring(0, precision);
let resultInt = parsed.integerPart;
// Round if needed
if (roundingValue >= 5) {
// Create a number from integer + fractional parts for rounding
const combinedNumber = resultInt + resultFrac;
const incrementedNumber = addOne(combinedNumber);
// Split back into integer and fractional parts
if (incrementedNumber.length > combinedNumber.length) {
// Carry occurred, integer part grew
resultInt = incrementedNumber.substring(0, incrementedNumber.length - precision);
resultFrac = incrementedNumber.substring(incrementedNumber.length - precision);
} else {
resultInt = incrementedNumber.substring(0, incrementedNumber.length - precision);
resultFrac = incrementedNumber.substring(incrementedNumber.length - precision);
}
}
// Remove trailing zeros from fractional part
resultFrac = resultFrac.replace(/0+$/, "");
// Construct result
let result = resultInt;
if (resultFrac !== "") {
result += "." + resultFrac;
}
return parsed.sign === -1 && result !== "0" ? "-" + result : result;
}