Description
Describe the enhancement requested
I really love the Arrow project and have gotten an enormous amount of value from it, and I realize this is essentially a duplicate of #100 but it's been a couple of years and I just want to reiterate how difficult it is to properly handle values with the Decimal
type from the JavaScript bindings. All you get is very low-level access to the underlying fixed-width buffer and you have to dig through the Arrow source to figure out how to do anything non-trivial with them. In my use-case all I need to do is accurately stringify them and have just had to fix the 3rd bug I've hit with my implementation (handling scale=0
). Here is what I've arrived at, but I wish I had more confidence that there aren't more bugs lurking (there's no way it handles Decimal256 properly, but I've never encountered them in the wild):
import { util } from "apache-arrow";
import { trimEnd } from "lodash";
function formatDecimal(value: Uint32Array, scale: number): string {
const negative = isNegative(value);
const sign = negative ? "-" : "";
if (negative) {
negate(value);
}
const str = util.bignumToString(new util.BN(value)).padStart(scale, "0");
if (scale === 0) {
return `${sign}${str}`;
}
const wholePart = str.slice(0, -scale) || "0";
const decimalPart = trimEnd(str.slice(-scale), "0") || "0";
return `${sign}${wholePart}.${decimalPart}`;
}
const MAX_INT32 = 2 ** 31 - 1;
function isNegative(value: Uint32Array): boolean {
// https://github.com/apache/arrow/blob/3600bd802fbf5eb0c6e00bdae7939bcc3d631eb1/cpp/src/arrow/util/basic_decimal.h#L125
return value[value.length - 1] > MAX_INT32;
}
function negate(value: Uint32Array): void {
// https://github.com/apache/arrow/blob/3600bd802fbf5eb0c6e00bdae7939bcc3d631eb1/cpp/src/arrow/util/basic_decimal.cc#L1144
let carry = 1;
for (let i = 0; i < value.length; i++) {
const elem = value[i];
const updated = ~elem + carry;
value[i] = updated;
carry &= elem === 0 ? 1 : 0;
}
}
Component(s)
JavaScript