জাভা স্ক্রিপ্ট থেকে সি # সংখ্যাগত যথার্থতা ক্ষতি


16

যখন জাভাস্ক্রিপ্ট এবং সি # এর মধ্যে মেসেজপ্যাকের সাহায্যে সিগন্যালআর ব্যবহার করে সিরিয়ালাইজিং এবং সি-র মধ্যে মানগুলি ডিেসরিয়ালাইজ করা হয় তখন আমি প্রাপ্তির শেষে সি # তে কিছুটা নির্ভুলতা হারাচ্ছি।

উদাহরণ হিসাবে আমি জাভাস্ক্রিপ্ট থেকে সি # তে 0.005 মান প্রেরণ করছি। যখন ডিসিরিয়ালাইজড মানটি সি # সাইডে উপস্থিত হয় আমি মানটি পাই 0.004999999888241291যা কাছাকাছি, তবে ঠিক 0.005 নয়। জাভাস্ক্রিপ্টের দিকের মানটি Numberএবং আমি ব্যবহার করছি সি # সাইডে double

আমি পড়েছি যে জাভাস্ক্রিপ্ট ভাসমান পয়েন্ট সংখ্যাগুলি ঠিক উপস্থাপন করতে পারে না যা ফলাফলের দিকে নিয়ে যেতে পারে 0.1 + 0.2 == 0.30000000000000004। আমি সন্দেহ করছি যে সমস্যাটি আমি দেখছি তা জাভাস্ক্রিপ্টের এই বৈশিষ্ট্যের সাথে সম্পর্কিত।

মজার অংশটি হ'ল আমি একই সমস্যাটিকে অন্যভাবে চলতে দেখছি না। সি # থেকে জাভাস্ক্রিপ্টে 0.005 পাঠানো জাভাস্ক্রিপ্টে 0.005 মানের ফলাফল করে।

সম্পাদনা করুন : সি # থেকে মানটি কেবল জেএস ডিবাগার উইন্ডোতে সংক্ষিপ্ত করা হয়েছে। @ পিট যেমন উল্লেখ করেছেন তেমন এটি এমন কিছুতে প্রসারিত হয় যা হুবহু 0.5 (0.005000000000000000104083408558) নয়। এর অর্থ হ'ল তফাত কমপক্ষে উভয় পক্ষেই ঘটে।

JSON সিরিয়ালাইজেশনের ক্ষেত্রে একই সমস্যা নেই কারণ আমি ধরে নিচ্ছি যে এটি স্ট্রিংয়ের মধ্য দিয়ে যায় যা প্রাপ্তি পরিবেশকে নিয়ন্ত্রণের কব্জায় ছেড়ে দেয় যা মানটিকে তার স্থানীয় সংখ্যা হিসাবে টাইপ করে।

আমি ভাবছি যে বাইনারি সিরিয়ালাইজেশন উভয় পক্ষের সাথে মানানসই মান আছে কি না।

যদি তা না হয় তবে এর মানে কি জাভাস্ক্রিপ্ট এবং সি # এর মধ্যে 100% সঠিক বাইনারি রূপান্তর করার কোনও উপায় নেই?

প্রযুক্তি ব্যবহৃত:

  • জাভাস্ক্রিপ্ট
  • । সিগন্যালআর এবং জিপপ্যাক 5 সহ নেট কোর

আমার কোড এই পোস্টের উপর ভিত্তি করে । পার্থক্যটি কেবলমাত্র আমিই ব্যবহার করছি ContractlessStandardResolver.Instance


সি # তে ভাসমান পয়েন্ট উপস্থাপনা প্রতিটি মানের জন্যও সঠিক নয়। সিরিয়ালযুক্ত ডেটা একবার দেখুন। আপনি কীভাবে এটি সি # তে পার্স করবেন?
জেফআরসন

আপনি সি # তে কোন ধরণের ব্যবহার করেন? ডাবল এ জাতীয় সমস্যা আছে বলে জানা যায়।
পল বাক

আমি অন্তর্নির্মিত মেসেজ প্যাক সিরিলাইজেশন / ডিসরিয়ালাইজেশন ব্যবহার করি যা সিগন্যালারের সাথে আসে এবং এটি বার্তা প্যাক ইন্টিগ্রেশন।
TGH

ভাসমান পয়েন্টের মানগুলি কখনই সুনির্দিষ্ট হয় না। আপনার যদি সুনির্দিষ্ট মানগুলির প্রয়োজন হয় তবে স্ট্রিং (ফর্ম্যাটিং ইস্যু) বা পূর্ণসংখ্যার (উদাহরণস্বরূপ 1000 দিয়ে গুণ করে) ব্যবহার করুন।
এটমিন

আপনি কি deserialized বার্তা চেক করতে পারেন? আপনি যে পাঠ্যটি জেএস থেকে পেয়েছেন, তার আগে সি # কোনও বস্তুকে রূপান্তরিত করে।
জনি পিয়াজি

উত্তর:


9

হালনাগাদ

এটি পরবর্তী প্রকাশে (5.0.0-প্রাকদর্শন 4) স্থির করা হয়েছে ।

আসল উত্তর

আমি পরীক্ষিত floatএবং double, এবং মজার ব্যাপার এই বিশেষ ক্ষেত্রে, শুধুমাত্র double, সমস্যা যেহেতু floatকাজ করা (অর্থাত 0.005 সার্ভারে পড়া হয়) বলে মনে হয়।

বার্তা বাইটগুলি পরিদর্শন করে পরামর্শ দেওয়া হয়েছে যে 0.005 টাইপ হিসাবে প্রেরণ করা হয় Float32Doubleযা 4-বাইট / 32-বিট আইইইই 754 একক নির্ভুলতা ফ্লোটিং পয়েন্ট নম্বর সত্ত্বেও Number64 বিট ভাসমান পয়েন্ট।

কনসোলে নিম্নলিখিত কোডটি চালিত করুন উপরোক্ত নিশ্চিত করেছেন:

msgpack5().encode(Number(0.005))

// Output
Uint8Array(5) [202, 59, 163, 215, 10]

mspack5 bit৪ বিট ভাসমান পয়েন্টকে বাধ্য করার জন্য একটি বিকল্প সরবরাহ করে:

msgpack5({forceFloat64:true}).encode(Number(0.005))

// Output
Uint8Array(9) [203, 63, 116, 122, 225, 71, 174, 20, 123]

তবে forceFloat64বিকল্পটি সিগন্যাল-প্রোটোকল-পিক্সপ্যাক ব্যবহার করে না ।

যদিও যে ব্যাখ্যা দিয়েছে কেন floatসার্ভার প্রান্তের কাজ করে, কিন্তু সত্যিই যে এখন হিসাবে জন্য একটি ফিক্স নয়মাইক্রোসফ্ট কি বলে অপেক্ষা করি ।

সম্ভাব্য কর্মক্ষেত্র

  • হ্যাক ?? প্যাক 5 বিকল্প? আপনার নিজের ফাইলপ্যাকটি কাঁটাচামচ করে সংকলন করুন ডিফল্টর সাথে forceFloat64সত্য ?? আমি জানি না।
  • floatসার্ভারের দিকে স্যুইচ করুন
  • stringউভয় পক্ষের ব্যবহার করুন
  • decimalসার্ভারের দিকে স্যুইচ করুন এবং কাস্টম লিখুন IFormatterProviderdecimalআদিম ধরণের নয়, এবং IFormatterProvider<decimal>জটিল ধরণের বৈশিষ্ট্যের জন্য ডাকা হয়
  • doubleসম্পত্তি মান পুনরুদ্ধার এবং double-> float-> decimal-> doubleকৌশল করতে পদ্ধতি সরবরাহ করুন method
  • অন্যান্য অবাস্তব সমাধানগুলি যা আপনি ভাবতে পারেন

টি এল; ডিআর

জেএস ক্লায়েন্টের সাথে সি # ব্যাকএন্ডে একক ফ্লোটিং পয়েন্ট নম্বর প্রেরণ করার সমস্যাটি একটি পরিচিত ভাসমান পয়েন্ট সমস্যা সৃষ্টি করে:

// value = 0.00499999988824129, crazy C# :)
var value = (double)0.005f;

doubleপদ্ধতিগুলির সরাসরি ব্যবহারের জন্য , সমস্যাটি একটি কাস্টম দ্বারা সমাধান করা যেতে পারে MessagePack.IFormatterResolver:

public class MyDoubleFormatterResolver : IFormatterResolver
{
    public static MyDoubleFormatterResolver Instance = new MyDoubleFormatterResolver();

    private MyDoubleFormatterResolver()
    { }

    public IMessagePackFormatter<T> GetFormatter<T>()
    {
        return MyDoubleFormatter.Instance as IMessagePackFormatter<T>;
    }
}

public sealed class MyDoubleFormatter : IMessagePackFormatter<double>, IMessagePackFormatter
{
    public static readonly MyDoubleFormatter Instance = new MyDoubleFormatter();

    private MyDoubleFormatter()
    {
    }

    public int Serialize(
        ref byte[] bytes,
        int offset,
        double value,
        IFormatterResolver formatterResolver)
    {
        return MessagePackBinary.WriteDouble(ref bytes, offset, value);
    }

    public double Deserialize(
        byte[] bytes,
        int offset,
        IFormatterResolver formatterResolver,
        out int readSize)
    {
        double value;
        if (bytes[offset] == 0xca)
        {
            // 4 bytes single
            // cast to decimal then double will fix precision issue
            value = (double)(decimal)MessagePackBinary.ReadSingle(bytes, offset, out readSize);
            return value;
        }

        value = MessagePackBinary.ReadDouble(bytes, offset, out readSize);
        return value;
    }
}

এবং সমাধান সমাধান করুন:

services.AddSignalR()
    .AddMessagePackProtocol(options =>
    {
        options.FormatterResolvers = new List<MessagePack.IFormatterResolver>()
        {
            MyDoubleFormatterResolver.Instance,
            ContractlessStandardResolver.Instance,
        };
    });

রেজোলভারটি নিখুঁত নয়, কারণ প্রক্রিয়াটি ধীর করে decimalদেওয়ার জন্য কাস্টিং হ'ল এটি বিপজ্জনক হতে পারেdouble

যাহোক

মন্তব্যে ওপি নির্দেশিত অনুসারে, জটিল প্রকারের ফেরত সম্পত্তি থাকার কারণে এটি সমস্যার সমাধান করতে পারে নাdouble

আরও তদন্তে মেসেজপ্যাক-সিএসের্পে সমস্যার কারণ প্রকাশিত হয়েছে:

// Type: MessagePack.MessagePackBinary
// Assembly: MessagePack, Version=1.9.0.0, Culture=neutral, PublicKeyToken=b4a0369545f0a1be
// MVID: B72E7BA0-FA95-4EB9-9083-858959938BCE
// Assembly location: ...\.nuget\packages\messagepack\1.9.11\lib\netstandard2.0\MessagePack.dll

namespace MessagePack.Decoders
{
  internal sealed class Float32Double : IDoubleDecoder
  {
    internal static readonly IDoubleDecoder Instance = (IDoubleDecoder) new Float32Double();

    private Float32Double()
    {
    }

    public double Read(byte[] bytes, int offset, out int readSize)
    {
      readSize = 5;
      // The problem is here
      // Cast a float value to double like this causes precision loss
      return (double) new Float32Bits(bytes, checked (offset + 1)).Value;
    }
  }
}

উপরের ডিকোডারটি যখন একটি একক floatসংখ্যায় রূপান্তর করতে হয় তখন ব্যবহৃত হয় double:

// From MessagePackBinary class
MessagePackBinary.doubleDecoders[202] = Float32Double.Instance;

v2

এই সমস্যাটি মেসেজপ্যাক-সিএসের্পের v2 সংস্করণে বিদ্যমান। আমি গিথুবে একটি ইস্যু করেছি , যদিও সমস্যাটি স্থির হতে যাচ্ছে না


আকর্ষণীয় অনুসন্ধান। এখানে একটি চ্যালেঞ্জটি হ'ল সমস্যাটি কোনও জটিল অবজেক্টে যে কোনও সংখ্যক দ্বৈত বৈশিষ্ট্যের ক্ষেত্রে প্রযোজ্য, তাই ডাবলকে সরাসরি লক্ষ্য করা আমার পক্ষে কঠিন y
TGH

@ টিজিএইচ হ্যাঁ, আপনি ঠিক বলেছেন। আমি বিশ্বাস করি এটি মেসেজপ্যাক-সিএসের্পে একটি বাগ। বিশদ জন্য আমার আপডেট দেখুন। আপাতত, আপনার floatএকটি workaround হিসাবে প্রয়োজন হতে পারে । আমি জানি না তারা এটি v2-তে স্থির করেছেন কিনা। আমি কিছু সময় পেলে একবার দেখে নেব। তবে সমস্যাটি v2 এখনও সিগন্যালআর সাথে সামঞ্জস্যপূর্ণ নয়। কেবলমাত্র সিগন্যালআর পূর্বরূপ সংস্করণ (5.0.0.0- *) v2 ব্যবহার করতে পারে।
ওয়েইচ

এটি v2 তেও কাজ করছে না। আমি বার্তা প্যাক-সিএসআরপ দিয়ে একটি বাগ উত্থাপন করেছি।
ওয়েইচ

@ টিজিএইচ দুর্ভাগ্যক্রমে গিথুব ইস্যুতে আলোচনার ভিত্তিতে সার্ভারের পক্ষে কোনও সমাধান হয়নি। সর্বোত্তম ফিক্সটি হবে ক্লায়েন্টের পক্ষে 32 বিটের চেয়ে 64 বিট প্রেরণ করা। আমি লক্ষ্য করেছি যে এটি করার জন্য জোর করার একটি বিকল্প রয়েছে, তবে মাইক্রোসফ্ট এটি প্রকাশ করে না (আমার বোঝার থেকে)। আপনি যদি কিছুটা দেখতে চান তবে কিছু দুষ্ট কাজের সাথে উত্তর আপডেট করুন। এবং এই ইস্যুতে শুভকামনা।
ওয়েইচ

এটি একটি আকর্ষণীয় সীসা মত শোনাচ্ছে। আমি এটি একবার দেখে নেব। এই সঙ্গে আপনার সাহায্যের জন্য ধন্যবাদ!
টিজিএইচ

14

আপনি আরও বড় নির্ভুলতায় প্রেরণ করছেন সঠিক মানটি পরীক্ষা করুন। ভাষা সাধারণত এটি আরও ভাল দেখানোর জন্য প্রিন্টের নির্ভুলতার সীমাবদ্ধ করে।

var n = Number(0.005);
console.log(n);
0.005
console.log(n.toPrecision(100));
0.00500000000000000010408340855860842566471546888351440429687500000000...

হ্যাঁ, আপনি যে সম্পর্কে সঠিক।
টিজিএইচ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.