كيفية التحقق مما إذا كان الزوج يحتوي على أكثر من عدد عشري n؟

StackOverflow https://stackoverflow.com/questions/264937

  •  06-07-2019
  •  | 
  •  

سؤال

حاليا لدي هذه الطريقة:

static boolean checkDecimalPlaces(double d, int decimalPlaces){
    if (d==0) return true;

    double multiplier = Math.pow(10, decimalPlaces); 
    double check  =  d * multiplier;
    check = Math.round(check);      
    check = check/multiplier; 
    return (d==check);      
}

ولكن هذه الطريقة تفشل ل checkDecmialPlaces(649632196443.4279, 4) ربما لأنني أقوم بالرياضيات على أساس 10 على رقم أساسي 2.

إذن كيف يمكن إجراء هذا الفحص بشكل صحيح؟

فكرت في الحصول على تمثيل سلسلة للقيمة المزدوجة ثم التحقق من ذلك باستخدام التعبير العادي - لكن ذلك بدا غريبًا.

يحرر:شكرا لجميع الإجابات.هناك حالات أحصل فيها بالفعل على ضعف، وفي هذه الحالات قمت بتنفيذ ما يلي:

private static boolean checkDecimalPlaces(double d, int decimalPlaces) {
    if (d == 0) return true;

    final double epsilon = Math.pow(10.0, ((decimalPlaces + 1) * -1));

    double multiplier = Math.pow(10, decimalPlaces);
    double check = d * multiplier;
    long checkLong = (long) Math.abs(check);
    check = checkLong / multiplier;

    double e = Math.abs(d - check);
    return e < epsilon;
}

لقد غيرت round إلى الاقتطاع.يبدو أن الحساب قد تم في round يزيد من عدم الدقة كثيرا.على الأقل في حالة الاختبار الفاشلة.
كما أشار البعض منكم، إذا كان بإمكاني الوصول إلى إدخال السلسلة "الحقيقي" الذي يجب أن أستخدمه BigDecimal للتحقق وهكذا فعلت:

BigDecimal decimal = new BigDecimal(value);
BigDecimal checkDecimal = decimal.movePointRight(decimalPlaces);
return checkDecimal.scale() == 0;

ال double القيمة التي أحصل عليها تأتي من Apache POI API التي تقرأ ملفات Excel.لقد أجريت بعض الاختبارات واكتشفت أنه على الرغم من عودة واجهة برمجة التطبيقات double قيم الخلايا الرقمية يمكنني الحصول على تمثيل دقيق عندما أقوم بتنسيق ذلك على الفور double مع ال DecimalFormat:

DecimalFormat decimalFormat = new DecimalFormat();
decimalFormat.setMaximumIntegerDigits(Integer.MAX_VALUE);
// don't use grouping for numeric-type cells
decimalFormat.setGroupingUsed(false);
decimalFormat.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US));
value = decimalFormat.format(numericValue);

يعمل هذا أيضًا مع القيم التي لا يمكن تمثيلها بالتنسيق الثنائي تمامًا.

هل كانت مفيدة؟

المحلول

فشل الاختبار، لأنك وصلت إلى دقة تمثيل الفاصلة العائمة الثنائية، والتي تبلغ حوالي 16 رقمًا IEEE754 دقة مزدوجة.سيؤدي الضرب في 649632196443.4279 في 10000 إلى اقتطاع التمثيل الثنائي، مما يؤدي إلى حدوث أخطاء عند التقريب والقسمة بعد ذلك، وبالتالي إبطال نتيجة الدالة تمامًا.

لمزيد من التفاصيل انظر http://en.wikipedia.org/wiki/Floating_point#Accuracy_problems

الطريقة الأفضل هي التحقق مما إذا كان n+1 المنازل العشرية أقل من عتبة معينة.لو d - round(d) اقل من epsilon (يرى حد)، التمثيل العشري ل d ليس لديه منازل عشرية كبيرة.وبالمثل إذا (d - round(d)) * 10^n اقل من epsilon, ، د يمكن أن يكون على الأكثر n أماكن مهمة.

يستخدم جون سكيتDoubleConverter للتحقق من الحالات التي d ليست دقيقة بما يكفي لاحتواء المنازل العشرية التي تبحث عنها.

نصائح أخرى

إذا كان هدفك هو تمثيل رقم بالضبط ن أرقام كبيرة على يمين العلامة العشرية، BigDecimal هو الفصل الذي يجب استخدامه.

غير قابلة للتغيير ، التعسفي الموقع أرقام عشرية.يتكون BigDecimal من قيمة عدد صحيح دقة تعسفية ومقياس عدد صحيح 32 بت.إذا صفر أو إيجابي ، فإن المقياس هو عدد الأرقام على يمين النقطة العشرية.إذا كانت السلبية ، يتم ضرب القيمة غير المقلدة للرقم بعشرة لسلطة نفي المقياس.وبالتالي فإن قيمة الرقم الذي يمثله BigDecimal هي (uncaledvalue × 10 على نطاق).

scale يمكن ضبطها عبر سيتسكيلي (كثافة العمليات)

كما هو الحال مع جميع العمليات الحسابية ذات الفاصلة العائمة، لا ينبغي عليك التحقق من المساواة، بل يجب عليك التحقق من أن الخطأ (إبسيلون) صغير بما فيه الكفاية.

إذا قمت باستبدال:

return (d==check);

مع شيء من هذا القبيل

return (Math.abs(d-check) <= 0.0000001);

يجب أن تعمل.من الواضح أنه يجب تحديد إبسيلون ليكون صغيرًا بدرجة كافية مقارنةً بعدد الكسور العشرية التي تبحث عنها.

ال double النوع هو رقم النقطة العائمة الثنائية.هناك دائمًا أخطاء واضحة في التعامل معها كما لو كانت أرقامًا عشرية عائمة.لا أعلم أنك ستتمكن يومًا من كتابة وظيفتك بحيث تعمل بالطريقة التي تريدها.

من المحتمل أن تضطر إلى العودة إلى المصدر الأصلي للرقم (ربما إدخال سلسلة) والاحتفاظ بالتمثيل العشري إذا كان ذلك مهمًا بالنسبة لك.

إذا كان بإمكانك التبديل إلى BigDecimal، فكما يوضح Ken G، هذا هو ما يجب أن تستخدمه.

إذا لم يكن الأمر كذلك، فسيتعين عليك التعامل مع مجموعة من المشكلات كما هو مذكور في الإجابات الأخرى.بالنسبة لي، أنت تتعامل مع رقم ثنائي (مزدوج) وتطرح سؤالاً حول التمثيل العشري لهذا الرقم؛أي أنك تسأل عن سلسلة.أعتقد أن حدسك صحيح.

لست متأكدًا من أن هذا ممكن التنفيذ حقًا بشكل عام.على سبيل المثال، كم عدد المنازل العشرية يفعل 1.0e-13 يملك؟ماذا لو كان ذلك ناتجًا عن بعض أخطاء التقريب أثناء إجراء العمليات الحسابية وهو في الحقيقة عادل 0 متنكر؟إذا كان الأمر كذلك، فمن ناحية أخرى فإنك تسأل عما إذا كان هناك أي أرقام غير الصفر في الأول ن المنازل العشرية يمكنك القيام بشيء مثل:

   static boolean checkDecimalPlaces(double d, unsigned int decimalPlaces){
      // take advantage of truncation, may need to use BigInt here
      // depending on your range
      double d_abs = Math.abs(d);
      unsigned long d_i = d_abs; 
      unsigned long e = (d_abs - d_i) * Math.pow(10, decimalPlaces);
      return e > 0;
   }

أعتقد أن هذا أفضل تحويل إلى سلسلة واستجواب قيمة الأسس

 public int calcBase10Exponet (Number increment)
 {
  //toSting of 0.0=0.0
  //toSting of 1.0=1.0
  //toSting of 10.0=10.0
  //toSting of 100.0=100.0
  //toSting of 1000.0=1000.0
  //toSting of 10000.0=10000.0
  //toSting of 100000.0=100000.0
  //toSting of 1000000.0=1000000.0
  //toSting of 1.0E7=1.0E7
  //toSting of 1.0E8=1.0E8
  //toSting of 1.0E9=1.0E9
  //toSting of 1.0E10=1.0E10
  //toSting of 1.0E11=1.0E11
  //toSting of 0.1=0.1
  //toSting of 0.01=0.01
  //toSting of 0.0010=0.0010  <== need to trim off this extra zero
  //toSting of 1.0E-4=1.0E-4
  //toSting of 1.0E-5=1.0E-5
  //toSting of 1.0E-6=1.0E-6
  //toSting of 1.0E-7=1.0E-7
  //toSting of 1.0E-8=1.0E-8
  //toSting of 1.0E-9=1.0E-9
  //toSting of 1.0E-10=1.0E-10
  //toSting of 1.0E-11=1.0E-11
  double dbl = increment.doubleValue ();
  String str = Double.toString (dbl);
//  System.out.println ("NumberBoxDefaultPatternCalculator: toSting of " + dbl + "=" + str);
  if (str.contains ("E"))
  {
   return Integer.parseInt (str.substring (str.indexOf ("E") + 1));
  }
  if (str.endsWith (".0"))
  {
   return str.length () - 3;
  }
  while (str.endsWith ("0"))
  {
   str = str.substring (0, str.length () - 1);
  }
  return - (str.length () - str.indexOf (".") - 1);
 }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top