bignum打印的问题,求个思路
发布于 8 年前 作者 p2227 3480 次浏览 来自 问答
let buf = Buffer.from([0xa4,0xfb,0x02,0x12,0x9a,0x90,0x7c,0x4a]);

这个buffer是一个小端的大整数,5367323847320337316buf.readUIntLE(0,8)只能得到5367323847320337000

如何得到正确的结果?求大概描述一下思路。

补充,这个问题可以用以下代码表示,把一个小端的十六进制字符串转化为一个十进制的大整数字符串

// return {@string}
function converHexStrToDecStrLittleEnd(str){
	//求这中间的实现思路
}

//test case 
assert(  converHexStrToDecStrLittleEnd('a4fb02129a907c4a') ===  '5367323847320337316' )

求 converHexStrToDecStrLittleEnd 这个函数怎么实现

10 回复

你的是读取无符号的, 如果是读取有符号的 let buf = Buffer.from([0xa4,0xfb,0x02,0x12,0x9a,0x90,0x7c,0x4a]); parseInt(buf.toString(‘hex’), 16)

@liujavamail小端 无符号 的,你这个方法不对的。 你的方法跟 buf.readUIntBE(0,8) 结果是一样的。没解决大整数的问题。

我大概写了个c++的扩展,但是只能支持到64位之前(long long),你给的那个数字是可以正常运算的

#include <node.h>
#include <stdlib.h>
#include <stdio.h>

namespace big {
	using v8::Exception;
	using v8::FunctionCallbackInfo;
	using v8::Isolate;
	using v8::Local;
	using v8::Number;
	using v8::Object;
	using v8::String;
	using v8::Value;
	
	void Add(const FunctionCallbackInfo<Value>& args) {
		Isolate* isolate = args.GetIsolate();

	    if (args.Length() < 2) {
			isolate->ThrowException(Exception::TypeError(
    		String::NewFromUtf8(isolate, "Wrong number of arguments")));
			return;
		}
		if (!args[0]->IsString() || !args[1]->IsString()) {
			isolate->ThrowException(Exception::TypeError(
    		String::NewFromUtf8(isolate, "Wrong arguments")));
			return;
		}

String::Utf8Value param1(args[0]->ToString());
String::Utf8Value param2(args[1]->ToString());
char *from1 = static_cast<char *>(*param1);
char *from2 = static_cast<char *>(*param2);

long long results = atoll(from1) + atoll(from2);

char buf[19] = "";
sprintf(buf, "%lld", results);

Local<String> tmp = String::NewFromUtf8(isolate, buf);
args.GetReturnValue().Set(tmp->ToString());
}

void Init(Local<Object> exports) {
	NODE_SET_METHOD(exports, "add", Add);
}

NODE_MODULE(abc, Init)
}

我觉得你遇到的问题思路有两个,第一个是转换成2进制后写一个位运算的公共方法,全部用二进制表示 第二个就是写c++扩展了,但是c++扩展的long long也仅支持64位,有点尴尬

有个bigint 裤包试一下。

@hyj1991 首先很感谢你的详细回复。 但我大概看了一下(具体V8 C++扩展的语法我清楚,猜的),你这个扩展是把两个字符串(buffer流)当作是数字进行相加吧,好像跟我的意思不太一样哦。我目前就是想把那串buffer代表的数字转成10进制打印出来。 1.这串c++代码解决大整数就是用long long的数据类型,而不是一个编程的思路 2.没处理小端的问题,还是说这个默认是用小端读取方式?

另外,你说【转换成2进制后写一个位运算的公共方法】是怎么做?我不是要计算哦,就是要转成10进制。

@stonephp 嗯,我的题意就是不用这些库,自己写代码怎么实现。

@p2227 我以为你是要做大整数的运算,在JS中我们没法控制数值保存地址采用大端模式还是小端模式吧 实际上JS里面是没有Int类型的,所谓的Number映射到V8里面都是IEEE-754的双精度浮点型,所以你出现的问题原因是在于这个数字大于2 ^53-1了,因此无论如何,要在JS中直接打印出这个大整数是不可能实现的 只能通过:

  • 输入十进制大数字的字符串
  • 字符串解析
  • 自己写的运算处理逻辑
  • 运算结果转成10进制字符串
  • 打印

这样的的方式来输出一个大数字吧

@hyj1991 你这样理解也行,我用代码表示吧

// return {@string}
function converHexStrToDecStrLittleEnd(str){
	//求这中间的实现思路
}

//test case 
assert(  converHexStrToDecStrLittleEnd('a4fb02129a907c4a') ===  '5367323847320337316' )

求 converHexStrToDecStrLittleEnd 这个函数怎么实现

@p2227,我确实没想到什么好办法 之前所说的二进制转化大致思路是这样的,但是大数字乘法没想到好的基于字符串的计算方式:

function convert(hex) {
	let binary = parseInt(hex, '16').toString(2).split('');
	let length = binary.length;
	//如果转换为的2进制长度小鱼等于53,直接转换返回
	if ((length - 53) <= 0) {
    	return parseInt(hex, '16').toString(10);
	}
	//将转换为的2进制数分割为前53位,以及其余的部分
	let arr1 = binary.filter((item, index)=>index < (length - 53)).join('');
	let arr2 = binary.filter((item, index)=>index >= (length - 53)).join('');

	//前53位可以直接求值
	let str1 = parseInt(arr1, '2').toString(10);
	//53位之后的求值后乘以阶数,这边没想到好办法转换成精确的十进制字符串
	let str2 = parseInt(arr2, '2').toString(10);
	return ((str1 * (Math.pow(2, 53) - 1) + str1) + Number(str2));
}

我感觉要纯粹使用js来处理这个转换,可能要写个字符串基本运算的公共方法,有空的话再想想吧

回到顶部