Sử dụng bitwise HOẶC 0 để tạo số


192

Một đồng nghiệp của tôi tình cờ phát hiện ra một phương pháp làm nổi số phao bằng cách sử dụng bitwise hoặc:

var a = 13.6 | 0; //a == 13

Chúng tôi đã nói về nó và tự hỏi một vài điều.

  • Làm thế nào nó hoạt động? Lý thuyết của chúng tôi là việc sử dụng một toán tử như vậy sẽ chuyển số thành một số nguyên, do đó loại bỏ phần phân số
  • Nó có bất kỳ lợi thế hơn làm Math.floor? Có lẽ nó nhanh hơn một chút? (chơi chữ không có ý định)
  • Liệu nó có bất kỳ nhược điểm? Có lẽ nó không hoạt động trong một số trường hợp? Rõ ràng là một điều hiển nhiên, vì chúng tôi phải tìm ra nó, và tốt, tôi đang viết câu hỏi này.

Cảm ơn.


6
Nhược điểm: nó chỉ hoạt động tối đa 2 ^ 31−1, khoảng 2 tỷ (10 ^ 9). Giá trị Số tối đa là khoảng 10 ^ 308 btw.
Vidime Vidas

12
Ví dụ: 3000000000.1 | 0ước tính đến -1294967296. Vì vậy, phương pháp này không thể được áp dụng cho tính toán tiền (đặc biệt trong trường hợp bạn nhân với 100 để tránh số thập phân).
Vidime Vidas

13
@ ŠimeVidas Floats cũng không nên được sử dụng trong tính toán tiền
George Reith

20
Nó không phải là sàn, nó là cắt ngắn (làm tròn về 0).
Bartłomiej Zalewski

3
@ Hậu quả hãy thử gõ 0.1 + 0.2 == 0.3vào bảng điều khiển JavaScript. Nếu ngôn ngữ của bạn hỗ trợ nó, bạn nên sử dụng loại thập phân. Nếu không, lưu trữ xu thay thế.
Alex Turpin

Câu trả lời:


160

Làm thế nào nó hoạt động? Lý thuyết của chúng tôi là việc sử dụng một toán tử như vậy sẽ chuyển số thành một số nguyên, do đó loại bỏ phần phân số

Tất cả các hoạt động bitwise ngoại trừ dịch chuyển phải không dấu >>>, hoạt động trên các số nguyên 32 bit đã ký. Vì vậy, sử dụng các phép toán bitwise sẽ chuyển đổi một số float thành một số nguyên.

Nó có bất kỳ lợi thế nào khi làm Math.floor không? Có lẽ nó nhanh hơn một chút? (chơi chữ không có ý định)

http://jsperf.com/or-vs-floor/2 có vẻ nhanh hơn một chút

Liệu nó có bất kỳ nhược điểm? Có lẽ nó không hoạt động trong một số trường hợp? Rõ ràng là một điều hiển nhiên, vì chúng tôi phải tìm ra nó, và tốt, tôi đang viết câu hỏi này.

  • Sẽ không vượt qua jsLint.
  • Số nguyên có chữ ký 32 bit
  • Hành vi so sánh kỳ lạ : Math.floor(NaN) === NaN, trong khi(NaN | 0) === 0

9
@harold thực sự, bởi vì nó không thực sự tròn, chỉ đơn giản là cắt ngắn.
Alex Turpin

5
Một nhược điểm khác có thể là Math.floor(NaN) === NaN, trong khi (NaN | 0) === 0. Sự khác biệt đó có thể quan trọng trong một số ứng dụng.
Ted Hopp

4
Jsperf của bạn đang mang lại thông tin hiệu suất cho các vòng lặp trống trên chrome do chuyển động mã bất biến vòng lặp. Một thử nghiệm hoàn hảo tốt hơn một chút sẽ là: jsperf.com/floor-performance/2
Sam Giles

4
Đây là một phần tiêu chuẩn của asm.js(nơi lần đầu tiên tôi biết về nó). Sẽ nhanh hơn nếu không vì lý do nào khác vì nó không gọi một chức năng trên Mathđối tượng, một chức năng có thể được thay thế bất cứ lúc nào như trong Math.floor = function(...).
gman

3
(value | 0) === valuecó thể được sử dụng để kiểm tra xem giá trị trên thực tế có phải là số nguyên không và chỉ có một số nguyên (như trong mã nguồn Elm @ dwayne-crooks được liên kết). Và foo = foo | 0có thể được sử dụng để ép buộc bất kỳ giá trị nào thành một số nguyên (trong đó các số 32 bit bị cắt bớt và tất cả các số không trở thành 0).
David Michael Gregg

36

Đây là cắt ngắn trái ngược với sàn. Câu trả lời của Howard là đúng; Nhưng tôi sẽ thêm vào Math.floorđó chính xác những gì nó được cho là liên quan đến số âm. Về mặt toán học, đó là những gì một sàn.

Trong trường hợp bạn mô tả ở trên, lập trình viên quan tâm nhiều hơn đến việc cắt bớt hoặc cắt hoàn toàn số thập phân. Mặc dù, cú pháp họ đã sử dụng loại che khuất thực tế rằng họ đang chuyển đổi float thành int.


7
Đây là câu trả lời đúng, chấp nhận một là không. Thêm vào đó Math.floor(8589934591.1)tạo ra kết quả mong đợi, 8589934591.1 | 0 KHÔNG .
Salman A

21

Trong ECMAScript 6, tương đương với |0Math.trunc , loại tôi nên nói:

Trả về phần tích phân của một số bằng cách loại bỏ bất kỳ chữ số phân số nào. Nó chỉ cắt bớt dấu chấm và các chữ số đằng sau nó, bất kể đối số là số dương hay số âm.

Math.trunc(13.37)   // 13
Math.trunc(42.84)   // 42
Math.trunc(0.123)   //  0
Math.trunc(-0.123)  // -0
Math.trunc("-1.123")// -1
Math.trunc(NaN)     // NaN
Math.trunc("foo")   // NaN
Math.trunc()        // NaN

6
Ngoại trừ thực tế là Math.trunc()hoạt động với số cao hơn hoặc bằng 2 ^ 31 và | 0không
Nolyurn

10

Điểm đầu tiên của bạn là chính xác. Số này được chuyển thành một số nguyên và do đó, bất kỳ chữ số thập phân nào cũng bị xóa. Xin lưu ý rằng, làm Math.floortròn đến số nguyên tiếp theo về âm vô cực và do đó cho kết quả khác khi áp dụng cho số âm.


5

Javascript đại diện Numbersố nổi 64 bit chính xác kép .

Math.floor làm việc với điều này trong tâm trí.

Hoạt động bitwise hoạt động trong số nguyên có chữ ký 32 bit . Số nguyên có chữ ký 32 bit sử dụng bit đầu tiên làm ký hiệu âm và 31 bit còn lại là số. Do đó, số lượng tối thiểu và tối đa cho phép các số đã ký 32 bit lần lượt là -2,147,483,648 và 2147483647 (0x7FFFFFFFF).

Vì vậy, khi bạn đang làm | 0, về cơ bản bạn đang làm & 0xFFFFFFFF. Điều này có nghĩa, bất kỳ số nào được biểu thị là 0x80000000 (2147483648) hoặc lớn hơn sẽ trở lại dưới dạng số âm.

Ví dụ:

 // Safe
 (2147483647.5918 & 0xFFFFFFFF) ===  2147483647
 (2147483647      & 0xFFFFFFFF) ===  2147483647
 (200.59082098    & 0xFFFFFFFF) ===  200
 (0X7FFFFFFF      & 0xFFFFFFFF) ===  0X7FFFFFFF

 // Unsafe
 (2147483648      & 0xFFFFFFFF) === -2147483648
 (-2147483649     & 0xFFFFFFFF) ===  2147483647
 (0x80000000      & 0xFFFFFFFF) === -2147483648
 (3000000000.5    & 0xFFFFFFFF) === -1294967296

Cũng thế. Hoạt động bitwise không "sàn". Họ cắt ngắn , giống như nói, họ làm tròn gần nhất 0. Khi bạn đi xung quanh các số âm, làm Math.floortròn xuống trong khi bitwise bắt đầu làm tròn lên .

Như tôi đã nói trước đây, Math.flooran toàn hơn vì nó hoạt động với các số nổi 64 bit. Bitwise nhanh hơn , có, nhưng giới hạn trong phạm vi đã ký 32 bit.

Để tóm tắt:

  • Bitwise hoạt động tương tự nếu bạn làm việc từ 0 to 2147483647.
  • Bitwise là 1 số nếu bạn làm việc từ -2147483647 to 0.
  • Bitwise hoàn toàn khác nhau đối với các số nhỏ hơn -2147483648và lớn hơn 2147483647.

Nếu bạn thực sự muốn điều chỉnh hiệu suất và sử dụng cả hai:

function floor(n) {
    if (n >= 0 && n < 0x80000000) {
      return n & 0xFFFFFFFF;
    }
    if (n > -0x80000000 && n < 0) {
      return (n - 1) & 0xFFFFFFFF;
    }
    return Math.floor(n);
}

Chỉ cần thêm Math.trunccác công việc như hoạt động bitwise. Vì vậy, bạn có thể làm điều này:

function trunc(n) {
    if (n > -0x80000000 && n < 0x80000000) {
      return n & 0xFFFFFFFF;
    }
    return Math.trunc(n);
}

5
  • Thông số kỹ thuật nói rằng nó được chuyển đổi thành một số nguyên:

    Đặt lnum là ToInt32 (lval).

  • Hiệu suất: điều này đã được thử nghiệm tại jsperf trước đây.

lưu ý: liên kết chết đến spec bị xóa

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.