Возможно, но далеко не просто. Вот набросок решения, которое могло бы быть приемлемым - написание и отладка его, вероятно, потребует как минимум дня согласованных усилий.
Пусть min
и max
будут примитив. Декабрь128 объекты из go .mongodb.org / mon go -driver / bson. Пусть MAXBITS
будет кратно 32; 128, вероятно, будет достаточным.
Получите значение (как большое.Инт) и показатель степени (как целое) min
и max
, используя метод BigInt
.
Совместите мин и макс так, чтобы они имели одинаковый показатель степени. Насколько это возможно, выровняйте по левому краю значение с большим показателем, уменьшив его показатель и добавив соответствующее число нулей к правой стороне его значения. Если это приведет к тому, что абсолютное значение значимости станет> = 2**(MAXBITS-1)
, то либо
- (а) сместите вправо значение с меньшим показателем, опустив цифры с правой стороны его Значение и увеличение его показателя приводит к потере точности.
- (b) Динамически увеличивать
MAXBITS
. - (c) Бросить ошибку.
В этот момент оба показателя будут одинаковыми, и оба значения будут выровнены большими целыми числами. Отложите показатели пока, и пусть range
(новый big.Int
) будет maxSignificand - minSignificand
. Это будет от 0 до 2**MAXBITS
.
Превратите range
в MAXBITS/32
uint32
с, используя методы Bytes
или DivMod
, что проще.
Если старшее слово range
равно math.MaxUint32
, тогда установите флаг limit
на false
, в противном случае true
.
Для n от 0 до MAXBITS/32
:
- , если
limit
истинно, используйте rand.Int63n
(!, , а не rand.Int31n
или rand.Uint32
), чтобы сгенерировать значение от 0 до n-го слова range
включительно, привести его к uint32
и сохранить его в качестве n-го слова выходных данных. Если сгенерированное значение равно n-му слову range
(т. Е. Если мы сгенерировали максимально возможное случайное значение для этого слова), то пусть limit
останется истинным, в противном случае установите его на ложное. - Если
limit
ложно, используйте rand.Uint32
, чтобы сгенерировать n-е слово для вывода. limit
остается ложным независимо от сгенерированного значения.
Объедините сгенерированные слова в big.Int
, построив []byte
и используя big/Int.SetBytes
или умножение и сложение , как удобно.
Добавьте сгенерированное значение к minSignificand
, чтобы получить значение и результат.
Используйте ParseDecimal128FromBigInt
с значение результата и показатель степени из шагов 2-3 для получения результата.
Сердцем алгоритма является шаг 6, который генерирует равномерное случайное целое число без знака произвольной длины 32 бита при время. Выравнивание на шаге 2 уменьшает проблему с плавающей запятой до целой, а вычитание на шаге 3 уменьшает ее до unsigned , так что нам нужно думать только об одной границе вместо 2 Флаг limit
записывает, имеем ли мы дело с этой границей или уже сузили результат до интервала, который его не включает.
Предостережения:
- Я не написал это, не говоря уже о том, чтобы проверить это. Возможно, я понял это неправильно. Приветствуется проверка работоспособности того, кто выполняет больше численных вычислений, чем я.
- Генерация чисел в большом динамическом диапазоне c (включая пересечение нуля) потеряет некоторую точность и пропустит некоторые возможные выходные значения с меньшим экспоненты, если не используется смехотворно большой
MAXBITS
; однако 128 битов должны давать результат, по крайней мере, такой же хороший, как наивный алгоритм, реализованный в виде десятичной дроби128. - Производительность, вероятно, довольно плохая.